React生命周期原理与用法踩坑笔记
React 是一个非常流行的前端框架,它有很多特色的 features,其中一项便是组件的生命周期。通过了解 React 组件的生命周期,可以更好的利用它提供的钩子函数,从而实现自己的需求。同时,在使用 React 开发中,我们也很容易遇到一些坑,接下来我们将从生命周期的原理以及踩坑经验两个方面,来分享 React 生命周期的攻略。
React 生命周期
基础概念
React 组件生命周期指的是从组件创建到销毁的整个过程,包括以下三个阶段:
- 挂载阶段(mounting):组件在创建时被挂载到 DOM 中;
- 更新阶段(updating):当组件的 props 或 state 发生改变时,会重新渲染并更新组件;
- 卸载阶段(unmounting):组件被从 DOM 中删除。
在这三个过程中,React 提供了一些钩子函数,可以帮助我们更好的控制组件的行为。下面是常见的钩子函数:
常见钩子函数
挂载阶段
- constructor(props):初始化 state 和绑定方法,props 和 state 都可以在 constructor 中访问。
- static getDerivedStateFromProps(props, state):从 props 中派生 state,返回值将会更新 state。
- render():返回 React 元素来构建虚拟 DOM 树。
- componentDidMount():组件已经被渲染到 DOM 中,在这个时刻可以进行一些异步操作、设置定时器等。
更新阶段
- static getDerivedStateFromProps(props, state):和挂载阶段一样,用于从 props 中派生 state。
- shouldComponentUpdate(nextProps, nextState):决定是否需要重新渲染组件,返回一个 bool 值。
- render():同挂载阶段。
- getSnapshotBeforeUpdate(prevProps, prevState):在 render 之后、更新 DOM 之前调用,用于获取新的状态或 props,通常用于滚动位置的保存。
- componentDidUpdate(prevProps, prevState, snapshot):在更新后被调用,可以进行 DOM 操作。
卸载阶段
- componentWillUnmount():组件被卸载时调用,可以进行一些清理工作(如计时器的清除、取消订阅等)。
示例说明
示例1:fetch 数据
在 componentDidMount 中请求后端 API 数据,更新 state,通过 props 向子组件传递数据以进行渲染。代码如下:
import React, { Component } from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = { data: [] };
}
async componentDidMount() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos');
const data = await response.json();
this.setState({ data });
}
render() {
return (
<ChildComponent data={this.state.data} />
);
}
}
export default ParentComponent;
示例2:滚动位置的保存
通过 getSnapshotBeforeUpdate 获取列表容器的 scrollTop 属性,再通过 componentDidUpdate 将列表容器的滚动位置保持到列表更新后的下次渲染中。代码如下:
import React, { Component } from 'react';
import ListItem from './ListItem';
class List extends Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
this.state = { data: [] };
this.intervalId = null;
}
componentDidMount() {
this.intervalId = setInterval(() => {
const data = this.state.data.slice();
data.push(Math.random());
this.setState({ data });
}, 1000);
}
getSnapshotBeforeUpdate() {
const listRef = this.listRef.current;
return listRef.scrollHeight - listRef.scrollTop;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot !== null) {
const listRef = this.listRef.current;
listRef.scrollTop = listRef.scrollHeight - snapshot;
}
}
componentWillUnmount() {
clearInterval(this.intervalId);
}
render() {
return (
<div ref={this.listRef}>
{this.state.data.map(item => (
<ListItem key={item} item={item} />
))}
</div>
);
}
}
export default List;
踩坑经验
在使用 React 过程中,我们也很容易碰到一些坑,接下来我们分享一些踩坑经验。
坑1:使用箭头函数
在声明函数时,使用箭头函数可以避免 this 问题的出现。当使用使用普通函数时, this 指向的是组件本身而非组件的实例。当从组件传递 function 系的 props 至子组件时,如果是普通函数就需要进行 bind 操作,而使用箭头函数则会自动绑定 this,避免了这个问题。示例代码如下:
class MyComponent extends Component {
handleClick() {
console.log(this); // undefined
}
render() {
return (
<button onClick={this.handleClick}>Click me!</button>
);
}
}
class App extends Component {
handleClick() {
console.log(this); // App component
}
render() {
return (
<MyComponent handleClick={() => this.handleClick()} />
);
}
}
坑2:避免在 shouldComponentUpdate 中使用 setState
在 shouldComponentUpdate 中进行 setState 操作会导致无限循环更新组件。在 shouldComponentUpdate 中应该避免使用 setState 来修改 state,因为 shouldComponentUpdate 中的 setState 不会再次调用 render 函数,而是直接进入 componentDidUpdate 导致死循环。如果需要在 shouldComponentUpdate 中修改 state,则需要使用 this.state 或 nextProps 进行计算。示例代码如下:
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.value === this.props.value && nextState.data === this.state.data) {
return false;
}
return true;
}
总结
本篇分享了 React 生命周期的原理和用法,并提供了两个示例代码演示了生命周期的应用。同时也分享了两个常见的踩坑经验,希望阅读者可以在开发中更加高效地使用 React。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:React生命周期原理与用法踩坑笔记 - Python技术站