React问答
本篇总结了React最基本和核心的一些概念
,通过简单的问答形式
来进行介绍。
React组件
什么是React组件呢?就是一个类
,或者说一个构造函数
。
class HelloMessage extends React.Component {
render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
}
ReactDOM.render(
<HelloMessage name="Olaf" />,
document.getElementById('hello')
);
注意:React使用了JSX
语法,其中,用大括号
来进行逻辑处理。
React元素
大致来说,React元素
分为两类:
- 纯html:<div>hello, 100</div>
- 自定义:<Hello />
打印出来如下:
它是一个对象,其中有props
、ref
、key
、type
等属性。它本质是通过 React.createElement()
来进行创建的。
props和state
- Props:为了解决父组件传值给子组件的问题
- State:是为了维护组件内部的数据状态
React常用的生命周期
React有一系列声明周期过程,其中最重要的一个是 componentDidMount
,它表明组件已经挂载到DOM节点上了 ,常用于ajax请求。与之对应的是 componentWillUnmount
,表明组件已经从DOM节点上卸载了,这时,可以进行清除事件等操作。
其次,还有 shouldComponentUpdate
,如果返回false,则组件不会更新渲染,常用于优化性能。
最后,请注意render()
方法的执行顺序,完整生命周期图如下:
当组件更新时,会执行哪些生命周期?
如上图,过程如下:
- shouldComponentUpdate
- componentWillUpdate
- render()
- componentDidUpdate
如果是父组件的props发生改变而导致的更新,那么在shouldComponentUpdate
发生之前,还会有componentWillReceiveProps
方法会被执行。
Ref
ref
可以绑定到任何组件上,获得该组件的真实DOM节点。如果不用ref,就只能通过获取DOM节点的方式,比如document.getElementById()
来获取DOM节点。
React中的循环渲染
在循环渲染时,需要注意记得指定key
:
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
一般使用map()
来进行映射渲染。
受控组件和不受控组件
将值直接用React的状态进行绑定,这样,组件就完全受React的状态控制了,称为受控组件
:
<input type="text" value={this.state.value} onChange={this.handleChange} />
如上代码中,input的值完全取决于state中的value
的值。
反之,其组件内部的值没有和React中的状态关联起来,来看一个例子:
<input type="text" ref={dom => this.input = dom} />
上面的input中,其自身就包含value值,其值并不受React中的state来管理和控制,为其自身所拥有,称为不受控组件
,可以通过 this.input.current.value
来获得其本身的值。
React中的内联样式
React的内联样式
是一个对象:
const style = {background: 'blue'}
<div style={style}>
但在实际开发中,更多地会使用命名css类
这种方式,常常搭配classnames
库来使用。
组件间的常见通信方式
React组件之间常常会需要相互间进行通信,根据其特征的不同,一般分为如下三种:
- 子组件通知父组件
这是最常见的方式,直接用 this.props.xxx() 事件即可。在父组件中捕获来自子组件的事件:
<ChildPart onChange={() => console.log('from child')}/>
子组件中的定义如下:
class ChildPart extends React.Component {
render() {
return (
<button onClick={() => this.props.onChange()}>
Hello {this.props.name}
</button>
);
}
}
在ChildPart
组件中,当点击button
时,触发onClick事件,引发this.props.onChange()
执行,从而触发事件到父组件中,最终打印from child
字符串。
- 父组件通知子组件
父组件可以使用props
来给子组件传值,但是props并不能直接触发子组件的事件。所以,需要通过ref
绑定子组件后,才可以直接访问子组件中的方法和属性。父组件的定义如下:
class Father extends React.Component {
render() {
return (
<div>
<ChlildPart ref={node => this.childPart = node} />
<button onClick={() => this.childPart.handleClick('father clicked button')}>Button</button>
</div>
);
}
}
定义了一个父类组件Father
,并用ref
将this.childPart
指向子组件ChildPart
的DOM节点,当点击父类中的button时,触发onClick
事件,接着执行this.childPart.handleClick('father clicked button')
,引发子组件中定义的handleClick
方法执行。
子组件ChildPart的定义如下:
class ChlildPart extends React.Component {
handleClick = value => {
console.log('msg from father: ', value)
}
render() {
return (
<h2>child part</h2>
);
}
}
- 没有嵌套关系的组件之间通信
可以使用events模块的 EventEmitter
类,来构建一个公共的event bus
来进行管理。
性能优化
React中的性能优化,核心的要点就是减少 render 的次数
,避免无谓的渲染,常用的处理方式有:
- 调整组件间的结构,使状态内移,防止父组件状态的更改触发过多子组件的渲染。
- PureRender,但PureRender只是浅比较。
setState后组件会马上更新吗?
不会,setState是异步
的。
组件开发的原则
组件就像积木
,在搭建房子之前,需要仔细考虑它们之间的关联。
React引入hooks的初衷是什么?
- 在组件之间复用状态逻辑很难
- 复杂组件变得难以理解
- 难以理解的 class
参考
- Hooks FAQ
- Introducing Hooks
- before you use memo | Dan Abramov
- 《深入React技术栈》陈屹著,人民邮电出版社