十、react进阶(事件处理和条件渲染)

1. 条件渲染

(1)if - else 条件渲染

1
2
3
4
5
6
7
8
9
export default function Greeting (props){
var flag = props.flag;
if(flag){
return <div>登录成功</div>
}
else{
return <div>登录失败</div>
}
}

(2)&& 逻辑与条件渲染

1
2
3
4
5
export default function Greeting (props){
return props.flag.length >0 && (props.flag.map(item =>{
return <p key={item}>{item}</p>
}))
}
  • 需要注意的是数组的函数传值是 通过 进行的,而不是把数组展开,那样子就会变成一个对象

(3)三元表达式渲染

1
2
3
export default function Greeting (props){
return props.flag==true?<div>登录成功</div>:<div>登陆失败</div>
}

(4)阻止组件渲染

1
2
3
4
5
6
7
8
export default function Greeting (props){
var flag = props.flag;
if(flag == false){

return null; ★★
}
return <div>登录成功</div>
}

2. 列表渲染

1
2
3
4
5
6
7
8
import React from 'react'
export default function List(props) {
const numbers = props.numbers;
const listItem = numbers.map(item => {
return <li key={item}>{item}</li>
})
return listItem.length > 0 ? <ul>{listItem}</ul> : null
}

2.1 Fragment 的使用 ★

n. 碎片; 片段;
v. (使) 碎裂,破裂,分裂
1
2
3
4
5
6
7
8
9
10
export default function List(props) {
const numbers = props.numbers;
const liItems = numbers.map(item => {
return <React.Fragment key={item.title}>
<dt>{item.title}</dt>
<dd>{item.msg}</dd>
</React.Fragment>
})
return liItems.length>0?(<dl>{liItems}</dl>):null;
}

jsx 表达式必须在一个根节点里面,使用 <React.Fragment>将其包裹在里面 。

2.2 react组合(类似vue中slot)

1
2
3
4
5
6
7
8
9
10
11
export default function FancyBorder(props) {
return <div className="child">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
{props.children}
</div>
}
1
2
3
4
ReactDom.render(<FancyBorder left={<div>左边内容</div>} right={<div>右边内容</div>}>
<span>这是span</span>
<p>这是p</p>
</FancyBorder>,document.getElementById("app"))

3. style 的使用

3.1 style.js

1
2
3
4
5
6
7
8
9
render(){
return
<h1 style= {{color:'red',fontSize:"18px",marginTop:"100px"}}>
这是评论列表组件
</h1>
}
// user: { fontSize: '14px' },
// 为什么两个{{}} ,可以理解成第一个{}为赋值已给一个 user 对象的括号 ,
// 再把这个 user 对象赋值给 style 即可 ,所以有两个 {{ }}
1
2
3
4
5
6
//写成 style.js 样式 , 变成独立的文件
export default {
item: { border: '1px dashed #ccc', margin: '10px', padding: '10px', boxShadow: '0 0 10px #ccc' },
user: { fontSize: '14px', paddingLeft:"10px"},
content: { fontSize: '12px',float:"right" }
}

3.2 css 样式表 ★

1.安装 css 加载器 ★

修改 webpack.config.js这个配置文件,为 css-loader 添加参数:

1
2
3
npm install  style-loader  css-loader -D

{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
  1. 导入样式表
1
2
3
4
5
6
7
8
9
10
11
import   "./cmtlist.css"

.title{
color: red;
text-align: center;
font-weight: 200;
}

h1{
font-style: italic;
}

css 模块化只针对类选择器 和 Id选择器生效, 不会对标签选择器模块化

3.开启模块作用域 ★

1
2
#1. webpack.config.js  修改css的rules规则,开启css的模块化
{ test: /\.css$/, use: ['style-loader', 'css-loader?modules'] } //modules表示为css启动模块化(启动模块化之后就有模块作用域)

4.css 模块化的注意点

1
2
3
4
/*默认情况下,所有的类和id样式都会被模块化*/
/*使用:local()包裹的样式会被模块化,但是对于标签名的样式无效 :local默认可以省略*/
/*使用:global包裹的样式不会被模块化*/
/*但是标签名的样式不会被模块化*/

一般在开发过程中,我们需要引入很多的第三方css文件,为了让第三方css文件能够被友好的使用,我们一般不会对css文件开启模块化,一般都会给自己写的less 、 sass文件开启模块化。★★★

5.用 :global()包裹的样式不会被模块化 , 使用:local() 包裹的样式会被模块化,但是对于标签名的样式无效,还有默认就是local,可以省略。

4. react的事件处理

  • 如果handleClick2是箭头函数,则函数中的this默认指向当前组件对象
  • 如果handleClick2是普通函数,则函数中的this默认指向undefine
1
<button onClick={this.handleClick2}>点我3</button>

4.1 this 的问题

修改事件函数中this指向问题:
1.直接声明函数为箭头函数,此时函数中的this指向当前组件对象
2.绑定事件的时候,通过bind方法来修改this指向

1
onClick={this.handleClick2.bind(this,'xx')}

​ 3.在构造函数中通过bind修改this指向

1
2
3
4
5
 constructor() {
super();
//在构造函数中改变this.handleClick2函数的this指向
this.handleClick2 = this.handleClick2.bind(this)
}

4.2 传参的问题★★

1.使用箭头函数传递参数

1
2
onClick={() => this.handleClick("click")
onClick={(e)=>this.handleClick2(e,"参数信息")}

差别在于第二个写法会传递一个事件对象过去,然后我们可以通过该事件对象阻止

默认事件等等 , e.preventDefault 阻止默认事件 。★

2.通过bind来传递参数

1
onClick={this.handleClick2.bind(this,'xx')}

缺陷是不能传递事件对象

5. 受控组件和非受控组件

5.1 受控组件

受控组件又称为智能组件,顾名思义受控组件就是受到控制的组件,受控组件是数据的所有者,它拥有数据、且拥有操作数据的action。它可以使用自己的state数据,也可以将数据和操作action传递给子组件,让子组件来完成UI或者功能。

1
2
3
4
5
6
<input type="text" value={this.state.age} onChange = {this.handleChange}/> 改不动的 value

handleChange = (e)=>{
this.setState({age:e.target.value})
console.log(this.state.age,"受控组件")
}

5.2 非受控组件

非受控组件不同于受控组件,它自身并不能拥有可改变的数据。因为函数组件只负责接收 props 并返回 UI,在真实的 React 应用开发场景下,我们经常尽可能的使用函数组件,将整个应用的 UI 拆分成尽可能小的视觉单元。

1
2
3
4
5
6
<input type="text" ref="txt" defaultValue={this.state.age} onChange = {this.handleChange2}/>

handleChange2 = (e)=>{
console.log(e.target.value,"这是非受控组件")
console.log(this.refs.txt.value,"这是ref绑定的值")
}

6. ref的用法

6.1 普通的用法

  • ref 绑定一个普通的字符串
1
2
3
4
5
6
7
{ ref绑定一个普通字符串*/}
<input type="text" ref="txt" defaultValue={this.props.default} onChange={this.handleChange2} />

handleChange2 = (e)=>{
console.log(e.target.value,"这是非受控组件")
console.log(this.refs.txt.value,"这是ref绑定的值")
}

6.2 React.createRef方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
constructor(){
super()
this.state = {
age:"21"
}
this.textInput = React.createRef(); ★
//在构造器中
}


handleChange3 =()=>{
console.log(this.textInput.current.value) ★★
//使用的方法 this.textInput.current.value
}

6.3 ref绑定回调函数

1
2
3
4
5
6
7
8
9
10
11
//ref绑定回调函数
<input type="text"
ref={input=>{this.input = input}} ★★★
// ref={input=>{this.input = input}} 使用箭头函数进行构造
defaultValue={this.props.default}
onChange={this.handleChange4} />

handleChange4 = ()=>{
console.log(this.input.value) ★
// 输出使用的方法
}

6.4 ref 的转发

Ref 转发是一个可选特性,其允许某些组件接收 ref,并将其向下传递(换句话说,“转发”它)给子组件。

简单解释:就是通过传递参数赋值的思想把父组件的 ref 赋值给子组件的 ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));

// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>点击我!</FancyButton>;

/*
以下是对上述示例发生情况的逐步解释:
1.我们通过调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量。
2.我们通过指定 ref 为 JSX 属性,将其向下传递给 <FancyButton ref={ref}>。
3.React 传递 ref 给 fowardRef 内函数 (props, ref) => ...,作为其第二个参数。
4.我们向下转发该 ref 参数到 <button ref={ref}>,将其指定为 JSX 属性。
5.当 ref 挂载完成,ref.current 将指向 <button> DOM 节点。
*/