React:组件通信、事件机制
一、组件、组件通信
组件,接收入参并返回用于描述页面的react元素。
1. class组件和函数(无状态)组件的区别
class组件和函数组件的转换如下:
- 定义一个同名的函数,参数是 props,
- 将 this.props 替换为 props,
- 去掉 render 方法。
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
function Welcome(props) {
return <h1>Hello, {props.name}</h1>
}
1.1 写法不同
1.2 函数组件捕获了渲染所使用的值:延迟输出
解释:react 中 props 是不可变的,但this一直是可变的。class组件每次调用this.props
时都会去获取最新的this值,因此无法保证当前读取到的props就是最初捕获到的。而函数组件props.user
会在函数执行的瞬间就被捕获,也就是说函数组件中 「延迟输出」 的state
是调用时的state
,而不是最新的state
。
class ProfilePage extends React.Component {
showMessage = () => {
alert('Followed ' + this.props.user);
};
handleClick = () => {
setTimeout(this.showMessage, 3000);
};
render() {
return <button onClick={this.handleClick}>Follow</button>;
}
}
React
的经典公式UI = f(data)
,也就是说UI
是当前应用状态的一个函数,那么事件处理程序也是渲染结果的一部分,它应该是一个拥有特定props
和state
的特定渲染。
然而对于类组件来说,调用一个回调函数读取一个 延迟的this.props
时,会打断这种关联,showMessage
回调并没有与任何一个特定的渲染绑定在一起,所以它失去了正确的props。也就是说,从this
中读取数据的这种行为,切断了这种联系。
-
怎么修复class组件的这个bug呢?可以提前读取
this.props
。 -
如果想要读取最新的props和state,而不是捕获时的数据,可以采用:
- class组件的this.props/this.state。
- useRef。
1.3 性能优化
- class组件:主要靠
shouldComponentUpdate
避免不必要的渲染。 - 函数组件:通过
React.memo
组件缓存渲染效果来提升性能。
如果组件在相同的props情况下渲染相同的结果,就可以用`React.memo`保存该结果,
下次React可以跳过渲染组件的操作直接复用结果。
const MyComponent = React.memo(function MyComponent(props) {
/* 使用 props 渲染 */
});
1.4 hooks之前的区别
- class组件可以有生命周期、状态管理(state、setState)和this,而函数组件不具备。
- 虽然class组件比较全面,但函数组件更灵活、容易测试、业务逻辑拆分也更方便。
2. 受控组件和非受控组件:针对表单元素
举个栗子~ :对于<input>
元素,它自己维护了一个内部的state
,能根据用户的输入自己进行UI更新(这个state不是我们看见的this.state
)。
如果我们想要控制输入框的内容,可以在this.state
上定义一个value
属性等于input
上的value
属性,然后用onChange
事件监听输入的变化并使用this.setState
更新this.state.value
。
<input value={this.state.value} onChange={this.handleChange} />
1.受控组件:像html
的<input>
等表单元素,我们通过将React 的 state
和表单元素的值建立依赖关系,再通过onChange
事件监听输入的变化并使用this.setState
更新state
。被React
以这种方式控制取值的表单输入元素就叫受控组件。
<input ref={eleRef} />
2.非受控组件:数据不受外部的state
控制,采用ref
引用从dom
中获取数据。
「React
推荐用受控组件
来实现表单。」
3. 五种组件通信的方式
- 父组件 => 子组件:
props
。
<Sub title = "早上好">
const Sub = (props) => { {props.title} }
- 子组件 => 父组件:
回调函数
,父组件将一个函数作为props传递给子组件,子组件调用该函数就可以向父组件通信。
<Sub callback = { this.callback.bind(this) } />
props.callback(msg)
-
隔代/跨级组件
- 中间组件层层传递
props
。 - 使用
context
。
- 中间组件层层传递
-
兄弟组件:将数据来源存放在它们共同的父级组件中
-
不相关的组件:
redux
进行全局状态管理。
二、react17 事件机制
- 原生事件:用
addEventListener
绑定的事件; - 合成事件:通过
JSX
绑定的事件,比如onClick={() => this.handleClick()}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this); // 方法绑定
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
e.preventDefault(); // 阻止默认行为
}
render() {
return (
// 1.小驼峰 2.传入的是一个函数作事件处理函数 3.不需要addEventListner,直接在初始渲染时添加监听器
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
合成事件 SyntheticEvent 是React
模拟原生dom
事件所有能力的一个事件对象,根据W3C规范
来定义合成事件,能够兼容所有浏览器。
const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
React
事件(handleClick
)不是直接绑定到dom
上面,而是绑定到渲染React
树的根DOM
容器中统一管理。- 当事件发生并冒泡至
根root
时,React
会使用统一的分发函数dispatchEvent
执行回调。
原生事件和合成事件的区别
- 事件命名方式不同:合成事件采用小驼峰式,原生事件是纯小写;
- 使用
JSX
语法时需要传入一个函数作为事件处理函数,而不是一个字符串。 - 阻止默认行为方式不同:原生事件可以返回
false
,React
需要preventDefault
。
参考文章
转载自:https://juejin.cn/post/7084915232149078052