react 总结+复习+应用加深
文章目录
- 一、React生命周期
- 1. 挂载阶段(Mounting)
- 补充
- 2. 更新阶段(Updating)
- 补充 static getDerivedStateFromProps 更新阶段应用
- 补充 getSnapshotBeforeUpdate
- 3. 卸载阶段(Unmounting)
- 二、React组件间的通信交互
- 1. 父子组件通信
- 2. 兄弟组件通信
- 三、React页面缓存机制应用
- 1. 使用`React.memo`进行组件缓存
- 2. 使用`shouldComponentUpdate`进行手动缓存控制
- 3. 使用第三方库(如`react - keep - alive`)
- 四、复杂商店应用(以Redux为例)
- 1. Redux核心概念
- 2. 在组件中使用Redux
- 生命周期钩子函数 getDerivedStateFromProps 的使用场景
- getDerivedStateFromProps与componentDidMount的区别
- getSnapshotBeforeUpdate与 shouldComponentUpdate 在更新阶段 调用顺序
- React Hook
- useState 和 useReducer 的区别
- 总结 (react 主要生命周期,常用生命周期,不常用生命周期,特殊环境下的生命周期)
一、React生命周期
1. 挂载阶段(Mounting)
- constructor(props)
- 执行时机:组件创建时调用,是类组件中第一个被调用的方法。
- 作用:
- 初始化组件的
state,通过this.state来设置初始状态。例如:this.state = { count: 0 };初始化一个计数器状态。 - 绑定事件处理函数。由于在JavaScript中,类方法默认不会绑定
this,所以需要手动绑定。如this.handleClick = this.handleClick.bind(this);。
- 初始化组件的
- 注意事项:
- 必须先调用
super(props),这是JavaScript类继承的要求,用于初始化父类的构造函数,确保组件能够正确访问props。
- 必须先调用
- componentWillMount(已废弃)
- 执行时机:在组件挂载到DOM之前,
render方法之前调用。 - 废弃原因:在异步渲染等场景下可能导致问题,并且它的一些功能可以被其他生命周期方法替代,如
componentDidMount。
- 执行时机:在组件挂载到DOM之前,
- render()
- 执行时机:挂载阶段和更新阶段都会调用。
- 作用:
- 用于描述组件的UI结构,返回一个React元素,这个元素可以是原生DOM元素(如
<div>、<p>等)或者是其他自定义组件。例如:return <div>Hello, World!</div>;。 - 根据组件的
props和state生成虚拟DOM(Virtual DOM),React会根据虚拟DOM来更新真实DOM。
- 用于描述组件的UI结构,返回一个React元素,这个元素可以是原生DOM元素(如
- 注意事项:
- 应该是一个纯函数,意味着它不应该修改组件的状态(
this.state),也不应该有其他副作用,如发送网络请求、修改DOM等。如果在render中修改状态,可能会导致无限循环的更新。
- 应该是一个纯函数,意味着它不应该修改组件的状态(
- componentDidMount()
- 执行时机:组件挂载到DOM之后立即调用。
- 作用:
- 适合进行需要DOM节点的操作。例如,通过
document.getElementById等DOM API获取DOM元素并进行操作。 - 发送网络请求获取数据来填充组件内容,因为此时组件已经挂载到DOM,可以安全地更新组件状态。例如:
- 适合进行需要DOM节点的操作。例如,通过
componentDidMount() {fetch('https://example.com/api/data').then(response => response.json()).then(data => this.setState({ data }));
}
- 初始化第三方JavaScript库。比如使用
Chart.js绘制图表,需要在DOM节点存在后进行初始化。
- 注意事项:- 注意清理在这个方法中创建的资源,如定时器、订阅等。可以在
componentWillUnmount中进行清理。
- 注意清理在这个方法中创建的资源,如定时器、订阅等。可以在
补充
-
static getDerivedStateFromProps
- 在组件挂载阶段,
static getDerivedStateFromProps是在constructor之后、render之前被调用。这使得它能够在组件首次渲染之前,根据传入的props对state进行初始化或者调整。 - 例如,在以下代码中:
class MyComponent extends React.Component {static getDerivedStateFromProps(props, state) {console.log('getDerivedStateFromProps called during mount');if (!state.initialized) {return {initialized: true,value: props.initialValue};}return null;}constructor(props) {super(props);console.log('Constructor called');this.state = {initialized: false};}render() {console.log('Render called');return <div>{this.state.value}</div>;} }- 当组件挂载时,控制台会先打印
Constructor called,然后是getDerivedStateFromProps called during mount,最后是Render called。可以看到getDerivedStateFromProps在constructor和render之间执行,用于根据props来初始化state中的value属性,并设置initialized为true。
- 在组件挂载阶段,
-
根据props初始化state
- 应用场景:当组件的初始状态依赖于从父组件传递过来的
props时,getDerivedStateFromProps是一个很好的工具。例如,一个显示用户信息的组件,其初始状态可能需要根据父组件传递的用户数据来设置。 - 示例代码:
class UserProfile extends React.Component {static getDerivedStateFromProps(props, state) {if (!state.userData) {return {userData: props.user};}return null;}constructor(props) {super(props);this.state = {};}render() {return (<div><p>Name: {this.state.userData.name}</p><p>Age: {this.state.userData.age}</p></div>);} }- 在这个
UserProfile组件中,state中的userData初始值是从props获取的。getDerivedStateFromProps检查state中是否已经有userData,如果没有(组件首次挂载时),就将props.user赋值给state.userData,然后在render函数中使用这个状态来显示用户的姓名和年龄。
- 应用场景:当组件的初始状态依赖于从父组件传递过来的
-
处理特殊的初始状态设置需求
- 应用场景:有时候,组件可能需要根据
props进行一些特殊的初始状态设置,比如对props进行转换或者验证后再存入state。例如,一个组件接收一个日期字符串格式的props,但在组件内部需要将其转换为日期对象存储在state中。 - 示例代码:
class DateDisplay extends React.Component {static getDerivedStateFromProps(props, state) {if (!state.dateObject) {const date = new Date(props.dateString);if (!isNaN(date.getTime())) {return {dateObject: date};} else {return {dateObject: null,error: 'Invalid date format'};}}return null;}constructor(props) {super(props);this.state = {};}render() {if (this.state.error) {return <p>{this.state.error}</p>;} else if (this.state.dateObject) {return <p>{this.state.dateObject.toDateString()}</p>;}return null;} }- 在
DateDisplay组件中,getDerivedStateFromProps检查state中是否已经有dateObject。如果没有(首次挂载时),它会尝试将props.dateString转换为日期对象。如果转换成功,就将日期对象存入state.dateObject;如果转换失败,就设置state.error来显示错误信息。然后在render函数中根据state的情况来显示日期或者错误信息。
- 应用场景:有时候,组件可能需要根据
2. 更新阶段(Updating)
- componentWillReceiveProps(nextProps)(已废弃)
- 执行时机:在组件接收到新的
props时被调用,在render方法之前。 - 废弃原因:可能会导致性能问题和意外行为,特别是在父组件频繁更新
props的情况下。React团队推荐使用getDerivedStateFromProps作为替代。
- 执行时机:在组件接收到新的
- shouldComponentUpdate(nextProps, nextState)
- 执行时机:在组件接收到新的
props或者state更新之前被调用。 - 作用:
- 用于性能优化。可以根据
nextProps和nextState与当前props和state的比较,决定组件是否需要重新渲染。例如:
- 用于性能优化。可以根据
- 执行时机:在组件接收到新的
shouldComponentUpdate(nextProps, nextState) {return nextProps.visible!== this.props.visible;
}
- 只有当返回
true时,组件才会继续更新流程(调用render等后续方法)。
- 注意事项:- 需要谨慎使用,因为错误的返回值可能导致组件不更新或者过度更新。
- componentWillUpdate(nextProps, nextState)(已废弃)
- 执行时机:在组件更新之前被调用,在
shouldComponentUpdate之后,render之前。 - 废弃原因:和
componentWillReceiveProps类似,可能导致难以预测的副作用,在异步渲染场景下会出现问题。
- 执行时机:在组件更新之前被调用,在
- render()(同挂载阶段的
render)。 - componentDidUpdate(prevProps, prevState)
- 执行时机:在组件更新后被调用,在
render之后。 - 作用:
- 可以根据更新前后的
props和state进行操作。例如,比较prevProps和this.props来判断某个属性是否改变,然后执行相应的操作。 - 对DOM进行操作,如更新第三方库的配置等。
- 可以根据更新前后的
- 注意事项:
- 注意避免在这个方法中引起无限循环更新。如果在
componentDidUpdate中更新状态,并且没有正确的条件限制,可能会导致组件不断地重新渲染。
- 注意避免在这个方法中引起无限循环更新。如果在
- 执行时机:在组件更新后被调用,在
补充 static getDerivedStateFromProps 更新阶段应用
- 更新阶段的调用机制
- 在组件更新阶段,每当组件接收到新的
props时,static getDerivedStateFromProps就会被调用。这是React为了确保组件的state能够根据新的props进行相应的更新而设计的机制。
-
根据新props更新state(保持state与props同步)
- 应用场景:当
props中的某些数据发生变化,并且组件的内部状态state需要与之保持同步时,getDerivedStateFromProps非常有用。例如,一个组件用于显示产品信息,props包含产品的价格和库存信息,当这些props更新时,组件的state也需要更新,以便正确地渲染。 - 示例代码:
class ProductDisplay extends React.Component {static getDerivedStateFromProps(props, state) {if (props.price!== state.price || props.stock!== state.stock) {return {price: props.price,stock: props.stock};}return null;}constructor(props) {super(props);this.state = {price: props.price,stock: props.stock};}render() {return (<div><p>Price: {this.state.price}</p><p>Stock: {this.state.stock}</p></div>);} }- 在这个例子中,当
props.price或者props.stock发生变化时,getDerivedStateFromProps会将新的props值更新到state中,然后render函数会根据新的state来重新渲染组件,显示更新后的价格和库存信息。
- 应用场景:当
-
根据props变化重置state的部分属性
- 应用场景:有时候,
props的变化可能需要重置state的某些属性。例如,一个表单组件可能有一个props来控制表单是否可编辑,当这个props变为不可编辑时,需要重置表单内部状态(如清除已输入但未提交的值)。 - 示例代码:
class EditableForm extends React.Component {static getDerivedStateFromProps(props, state) {if (props.isEditable!== state.isEditable) {if (!props.isEditable) {return {isEditable: props.isEditable,inputValue: ''};}return {isEditable: props.isEditable};}return null;}constructor(props) {super(props);this.state = {isEditable: props.isEditable,inputValue: ''};}handleChange = (e) => {if (this.state.isEditable) {this.setState({inputValue: e.target.value});}};render() {return (<inputtype="text"value={this.state.inputValue}onChange={this.handleChange}disabled={!this.state.isEditable}/>);} }- 在这里,当
props.isEditable发生变化时,getDerivedStateFromProps会检查它与state.isEditable是否一致。如果props.isEditable变为false,就会重置state.inputValue为'',同时更新state.isEditable。在render函数中,input元素的disabled属性会根据state.isEditable来设置,value属性会根据state.inputValue来设置,并且handleChange函数只有在state.isEditable为true时才会更新state.inputValue。
- 应用场景:有时候,
-
结合shouldComponentUpdate优化更新性能(高级用法)
- 应用场景:在某些复杂的组件中,为了避免不必要的重新渲染,可以结合
shouldComponentUpdate生命周期方法和getDerivedStateFromProps来优化性能。例如,当props的变化只影响state的部分属性,而这些属性的变化又不影响组件的渲染结果时,可以通过shouldComponentUpdate来阻止组件重新渲染。 - 示例代码:
class ComplexComponent extends React.Component {static getDerivedStateFromProps(props, state) {if (props.someProp!== state.someProp) {return {someProp: props.someProp};}return null;}shouldComponentUpdate(nextProps, nextState) {if (this.props.otherProp === nextProps.otherProp && this.state.someProp === nextState.someProp) {return false;}return true;}constructor(props) {super(props);this.state = {someProp: props.someProp};}render() {return (<div><p>{this.state.someProp}</p><p>{this.props.otherProp}</p></div>);} }- 在这个
ComplexComponent中,getDerivedStateFromProps负责根据props.someProp的变化更新state.someProp。shouldComponentUpdate会检查props.otherProp和state.someProp是否发生变化,如果没有变化,就返回false,阻止组件重新渲染,从而提高性能。在组件更新阶段,getDerivedStateFromProps会先于shouldComponentUpdate被调用
- 应用场景:在某些复杂的组件中,为了避免不必要的重新渲染,可以结合
补充 getSnapshotBeforeUpdate
-
阶段位置
getSnapshotBeforeUpdate生命周期方法在组件更新阶段被调用,具体是在render方法之后,componentDidUpdate之前。这个位置使得它能够获取到组件更新前的最后一个“快照”(DOM状态或其他相关信息),并将其传递给componentDidUpdate。
-
应用场景
-
保存滚动位置
- 场景描述:在一个可滚动的列表组件中,当列表数据更新(例如添加或删除了列表项)时,为了保持用户的滚动位置,需要在更新前获取滚动位置,然后在更新后恢复滚动位置。
- 示例代码:
class ScrollableList extends React.Component {constructor(props) {super(props);this.listRef = React.createRef();this.state = {items: [],scrollTop: 0};}componentDidMount() {// 模拟获取初始数据this.setState({items: [1, 2, 3, 4, 5]});}handleAddItem = () => {const newItems = [...this.state.items, this.state.items.length + 1];this.setState({items: newItems});};getSnapshotBeforeUpdate(prevProps, prevState) {if (this.listRef.current) {return this.listRef.current.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {if (snapshot!== null) {this.listRef.current.scrollTop = snapshot;}}render() {return (<div ref={this.listRef} style={{ height: '200px', overflow: 'auto' }}><ul>{this.state.items.map((item) => (<li key={item}>{item}</li>))}</ul><button onClick={this.handleAddItem}>Add Item</button></div>);} }- 解释:在这个
ScrollableList组件中,getSnapshotBeforeUpdate用于在列表更新前获取div元素(通过ref获取)的滚动位置scrollTop。这个滚动位置信息作为snapshot参数传递给componentDidUpdate,在componentDidUpdate中恢复滚动位置,从而实现当列表更新时,用户的滚动位置保持不变。
-
记录元素尺寸变化
- 场景描述:当组件中的某个元素尺寸(如宽度、高度)因为状态或属性更新而发生变化时,可以在
getSnapshotBeforeUpdate中记录旧的尺寸,然后在componentDidUpdate中比较新旧尺寸,执行相应的操作,例如调整其他元素的布局。 - 示例代码:
class ResizableComponent extends React.Component {constructor(props) {super(props);this.componentRef = React.createRef();this.state = {width: '100px',height: '100px'};}handleResize = () => {this.setState((prevState) => ({width: `${prevState.width.slice(0, -2) * 1.2}px`,height: `${prevState.height.slice(0, -2) * 1.2}px`}));};getSnapshotBeforeUpdate(prevProps, prevState) {if (this.componentRef.current) {return {prevWidth: this.componentRef.current.offsetWidth,prevHeight: this.componentRef.current.offsetHeight};}return null;}componentDidUpdate(prevProps, prevState, snapshot) {if (snapshot) {const widthChange = this.componentRef.current.offsetWidth - snapshot.prevWidth;const heightChange = this.componentRef.current.offsetHeight - snapshot.prevHeight;console.log(`Width change: ${widthChange}px, Height change: ${heightChange}px`);}}render() {return (<divref={this.componentRef}style={{width: this.state.width,height: this.state.height,backgroundColor: 'lightblue',cursor: 'pointer'}}onClick={this.handleResize}/>);} }- 解释:在
ResizableComponent组件中,getSnapshotBeforeUpdate获取组件div元素(通过ref获取)更新前的宽度和高度。在componentDidUpdate中,通过比较更新前后的宽度和高度,计算出尺寸变化,并打印到控制台。这可以用于进一步的布局调整或其他与尺寸变化相关的操作。
- 场景描述:当组件中的某个元素尺寸(如宽度、高度)因为状态或属性更新而发生变化时,可以在
-
3. 卸载阶段(Unmounting)
- componentWillUnmount()
- 执行时机:在组件从DOM中移除之前被调用。
- 作用:
- 用于清理在
componentDidMount或其他生命周期中创建的副作用。例如:- 清除定时器:
clearInterval(this.timer);,如果在componentDidMount中设置了定时器,需要在这里清除。 - 取消网络请求:如果使用了一些没有自动取消机制的网络请求库,需要手动取消请求。
- 取消订阅:如取消对Redux store或者事件总线的订阅。
- 清除定时器:
- 用于清理在
- 注意事项:
- 忘记清理副作用可能会导致内存泄漏、性能下降或者其他意外行为。
二、React组件间的通信交互
1. 父子组件通信
- 父组件向子组件传递数据(通过
props)- 方式:在父组件的
render方法中,将数据作为属性传递给子组件。例如,父组件有一个数据message,传递给子组件ChildComponent:
- 方式:在父组件的
class ParentComponent extends React.Component {constructor(props) {super(props);this.state = {message: 'Hello from parent'};}render() {return <ChildComponent message={this.state.message} />;}
}
class ChildComponent extends React.Component {render() {return <div>{this.props.message}</div>;}
}
- 子组件向父组件通信(通过回调函数)
- 方式:父组件将一个回调函数作为
props传递给子组件,子组件在需要的时候调用这个回调函数,并将数据传递给父组件。例如,子组件中有一个按钮,点击按钮后将数据传递给父组件:
- 方式:父组件将一个回调函数作为
class ParentComponent extends React.Component {constructor(props) {super(props);this.state = {childData: null};}handleChildData = (data) => {this.setState({ childData: data });};render() {return (<div><ChildComponent sendDataToParent={this.handleChildData} /><div>{this.state.childData}</div></div>);}
}
class ChildComponent extends React.Component {handleClick = () => {const data = 'Data from child';this.props.sendDataToParent(data);};render() {return <button onClick={this.handleClick}>Send Data to Parent</button>;}
}
2. 兄弟组件通信
- 通过共同的父组件作为中间人
- 方式:兄弟组件A和B,A要给B传递数据。A通过调用父组件传递过来的回调函数将数据传递给父组件,父组件再将数据通过
props传递给B。例如:
- 方式:兄弟组件A和B,A要给B传递数据。A通过调用父组件传递过来的回调函数将数据传递给父组件,父组件再将数据通过
class ParentComponent extends React.Component {constructor(props) {super(props);this.state = {dataFromA: null};}handleDataFromA = (data) => {this.setState({ dataFromA: data });};render() {return (<div><SiblingA sendDataToParent={this.handleDataFromA} /><SiblingB dataFromA={this.state.dataFromA} /></div>);}
}
class SiblingA extends React.Component {handleClick = () => {const data = 'Data from Sibling A';this.props.sendDataToParent(data);};render() {return <button onClick={this.handleClick}>Send Data</button>;}
}
class SiblingB extends React.Component {render() {return <div>{this.props.dataFromA}</div>;}
}
- 使用状态管理库(如Redux、Mobx)
- 方式:
- 以Redux为例,组件通过
connect函数连接到Redux store,dispatch操作来触发状态改变,其他组件可以通过订阅store的状态更新来获取最新数据。 - 首先定义
action(描述发生的操作),例如:
- 以Redux为例,组件通过
- 方式:
const ADD_ITEM = 'ADD_ITEM';
export const addItem = (item) => ({type: ADD_ITEM,payload: item
});
- 然后定义
reducer(根据action更新状态),例如:
const initialState = {items: []
};
const itemReducer = (state = initialState, action) => {switch (action.type) {case ADD_ITEM:return {...state,items: [...state.items, action.payload]};default:return state;}
};
- 在组件中使用:
- 发送数据的组件:
import React from 'react';
import { connect } from 'react-redux';
import { addItem } from './actions';
class ItemSender extends React.Component {handleSendItem = () => {const newItem = 'New Item';this.props.addItem(newItem);};render() {return <button onClick={this.handleSendItem}>Send Item</button>;}
}
const mapDispatchToProps = (dispatch) => ({addItem: (item) => dispatch(addItem(item))
});
export default connect(null, mapDispatchToProps)(ItemSender);
- 接收数据的组件:
import React from 'react';
import { connect } from 'react-redux';
class ItemReceiver extends React.Component {render() {return (<div>{this.props.items.map((item, index) => (<div key={index}>{item}</div>))}</div>);}
}
const mapStateToProps = (state) => ({items: state.items
});
export default connect(mapStateToProps, null)(ItemReceiver);
三、React页面缓存机制应用
1. 使用React.memo进行组件缓存
- 原理:
React.memo是一个高阶组件,它会对组件的props进行浅比较。如果props没有改变,组件就不会重新渲染,从而实现缓存效果。 - 应用场景:对于纯展示组件,它们的渲染只依赖于
props,并且重新渲染成本较高(如组件内部有复杂的计算或者渲染大量子元素)。例如:
const MyComponent = React.memo((props) => {console.log('MyComponent is rendering');return <div>{props.text}</div>;
});
- 注意事项:
- 只是对
props进行浅比较,对于复杂的数据结构(如嵌套对象或数组),可能会出现即使数据内容改变,但浅比较认为没有改变的情况。可以通过自定义比较函数来解决这个问题,React.memo可以接受第二个参数,一个比较函数。例如:
- 只是对
const areEqual = (prevProps, nextProps) => {return prevProps.text === nextProps.text;
};
const MyComponent = React.memo((props) => {console.log('MyComponent is rendering');return <div>{props.text}</div>;
}, areEqual);
2. 使用shouldComponentUpdate进行手动缓存控制
- 原理:在组件内部实现
shouldComponentUpdate生命周期方法,通过比较新的props和state与旧的props和state,来决定组件是否需要重新渲染。 - 应用场景:当
React.memo的浅比较不能满足需求,或者需要更精细地控制组件渲染时。例如,一个组件有多个props,但只有其中一个props的改变会影响渲染,就可以在shouldComponentUpdate中进行判断:
class MyComponent extends React.Component {shouldComponentUpdate(nextProps, nextState) {return nextProps.importantValue!== this.props.importantValue;}render() {return <div>{this.props.importantValue}</div>;}
}
- 注意事项:
- 需要谨慎实现,错误的比较逻辑可能导致组件不更新或者过度更新。
3. 使用第三方库(如react - keep - alive)
- 原理:这些库通常会在组件卸载时将组件的状态保存起来,当组件再次挂载时恢复状态,实现缓存效果。
- 应用场景:在复杂的单页应用中,对于那些切换频繁但状态需要保留的组件很有用。例如,在一个多标签页的应用中,当切换离开某个标签页(组件卸载),再次切换回来时(组件挂载),组件的状态(如表单输入内容、滚动位置等)能够恢复。
- 注意事项:
- 不同的第三方库有不同的使用方式和限制,需要仔细阅读文档。例如,有些库可能对组件的结构或者状态管理方式有特定的要求。
四、复杂商店应用(以Redux为例)
1. Redux核心概念
- Store
- 定义:它是一个单一的数据源,存储整个应用的状态。通过
createStore函数(或者在一些高级配置中使用configureStore等)创建。例如:
- 定义:它是一个单一的数据源,存储整个应用的状态。通过
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
- 注意事项:
- 整个应用应该只有一个
store,以保证状态的一致性。 store是不可变的,不能直接修改store中的状态,只能通过发送action来触发reducer更新状态。
- 整个应用应该只有一个
- Reducer
- 定义:它是一个纯函数,用于根据
action来更新store中的状态。它接收当前状态和一个action作为参数,并返回新的状态。例如:
- 定义:它是一个纯函数,用于根据
const initialState = {count: 0
};
const counterReducer = (state = initialState, action) => {switch (action.type) {case 'INCREMENT':return {...state,count: state.count + 1};case 'DECREMENT':return {...state,count: state.count - 1};default:return state;}
};
- 注意事项:
- 必须是纯函数,即对于相同的输入(当前状态和action),必须返回相同的输出(新的状态)。
- 不能直接修改传入的state参数,应该返回一个新的状态对象,可以使用对象展开运算符(...)来创建新的对象。 - Action
- 定义:它是一个包含
type属性的JavaScript对象,用于描述发生了什么操作。type属性通常是一个字符串常量,用于在reducer中识别不同的操作。例如:
- 定义:它是一个包含
const incrementAction = {type: 'INCREMENT'
};
- 注意事项:
type字段应该是唯一且具有描述性的,以便在reducer中能够准确地处理不同的操作。- 除了
type字段,还可以包含其他数据(通过payload等字段)来传递操作所需的信息。
2. 在组件中使用Redux
- 通过
connect函数连接组件和store(在React - Redux库中)- 方式:
connect函数用于将组件与Redux的store连接起来,它接受两个参数:mapStateToProps和mapDispatchToProps。mapStateToProps用于将store中的状态映射为组件的props。例如:
- 方式:
const mapStateToProps = (state) => ({count: state.count
});
mapDispatchToProps用于将dispatch函数(用于触发action)映射为组件的props。例如:
const mapDispatchToProps = (dispatch) => ({increment: () => dispatch({ type: 'INCREMENT' }),decrement: () => dispatch({ type: 'DECREMENT' })
});
- 注意事项:
-mapStateToProps和mapDispatchToProps都是可选的。如果组件只需要获取状态,只需要定义mapStateToProps
生命周期钩子函数 getDerivedStateFromProps 的使用场景
- constructor()
- 这是挂载阶段首先执行的函数。它主要用于初始化组件的状态(
this.state)和绑定事件处理函数。例如:
class MyComponent extends React.Component {constructor(props) {super(props);this.state = {count: 0};// 绑定事件处理函数this.handleClick = this.handleClick.bind(this);}handleClick() {// 处理点击事件,更新状态等操作this.setState((prevState) => ({count: prevState.count + 1}));}//... } - 这是挂载阶段首先执行的函数。它主要用于初始化组件的状态(
- getDerivedStateFromProps()(如果定义)
- 这个函数在
constructor之后、render之前调用。它用于根据props的变化来更新state,返回一个对象来更新state,或者返回null表示不需要更新。例如:
class MyComponent extends React.Component {static getDerivedStateFromProps(props, state) {if (props.someProp!== state.someProp) {return {someProp: props.someProp};}return null;}//... } - 这个函数在
- render()
- 在
getDerivedStateFromProps(如果有)之后执行。它是必需的方法,用于描述组件的UI结构,返回一个React元素(可以是原生DOM元素或者其他自定义组件)。例如:
class MyComponent extends React.Component {//...render() {return (<div><p>Count: {this.state.count}</p><button onClick={this.handleClick}>Increment</button></div>);} } - 在
- componentDidMount()
- 在组件挂载到DOM后立即执行。这个阶段适合进行一些需要DOM节点的操作,如发送网络请求获取数据填充组件内容、添加订阅、初始化第三方JavaScript库等。例如:
class MyComponent extends React.Component {//...componentDidMount() {// 发送网络请求fetch('https://example.com/api/data').then(response => response.json()).then(data => this.setState({data}));} }
所以,挂载阶段生命周期钩子函数的一般执行顺序是:constructor() -> getDerivedStateFromProps()(如果定义) -> render() -> componentDidMount()。需要注意的是,如果没有定义getDerivedStateFromProps,则直接从constructor跳到render。
getDerivedStateFromProps与componentDidMount的区别
- 执行时机
- getDerivedStateFromProps:在组件实例化(通过构造函数
constructor创建)之后、render方法之前调用,并且在组件每次接收到新的props时也会被调用。这意味着它在组件的初始挂载以及后续props更新时都会介入。 - componentDidMount:在组件挂载到DOM之后才会被调用,也就是在
render方法执行完成,且组件对应的真实DOM节点已经插入到文档(Document)之后。它只会在组件初始挂载时执行一次。
- getDerivedStateFromProps:在组件实例化(通过构造函数
- 功能用途
- getDerivedStateFromProps:
- 主要用于根据
props的值来更新或派生组件的state。它是一个静态方法,不能访问组件实例(即不能使用this关键字)。例如,当props中的某个数据需要同步到state,以便在组件内部进行进一步处理或者在render方法中使用更新后的状态来生成UI时,就可以使用这个方法。
class MyComponent extends React.Component {static getDerivedStateFromProps(props, state) {if (props.value!== state.value) {return { value: props.value };}return null;}constructor(props) {super(props);this.state = { value: props.value };}//... }- 也用于在
props变化时,对state进行有条件的更新,以确保state与props之间的某种同步关系。
- 主要用于根据
- componentDidMount:
- 通常用于执行那些需要DOM节点已经存在才能进行的操作。例如,发送网络请求获取数据来填充组件内容。
class DataFetcher extends React.Component {constructor(props) {super(props);this.state = { data: null };}componentDidMount() {fetch('https://example.com/api/data').then(response => response.json()).then(data => this.setState({ data }));}//... }- 还用于初始化第三方JavaScript库,比如使用
Chart.js在页面中绘制图表,或者添加事件监听器等操作,这些操作都依赖于组件已经挂载到DOM上。
- getDerivedStateFromProps:
- 使用限制和注意事项
- getDerivedStateFromProps:
- 由于是静态方法,它不能直接访问组件实例的属性和方法。这意味着不能在其中调用
this.setState来触发异步操作或者更新其他非派生自props的状态。 - 必须返回一个对象用于更新
state或者返回null表示不需要更新state。返回值会直接与当前state进行合并。
- 由于是静态方法,它不能直接访问组件实例的属性和方法。这意味着不能在其中调用
- componentDidMount:
- 因为这个方法在组件挂载后执行,所以在其中进行的操作(如添加事件监听器、创建定时器等)需要在组件卸载时进行清理,以避免内存泄漏等问题。通常在
componentWillUnmount方法中进行这些清理操作。
- 因为这个方法在组件挂载后执行,所以在其中进行的操作(如添加事件监听器、创建定时器等)需要在组件卸载时进行清理,以避免内存泄漏等问题。通常在
- getDerivedStateFromProps:
getSnapshotBeforeUpdate与 shouldComponentUpdate 在更新阶段 调用顺序
-
生命周期调用顺序
- 在组件更新阶段,
shouldComponentUpdate会先于getSnapshotBeforeUpdate被调用。 shouldComponentUpdate是在组件接收到新的props或者state发生变化后,在重新渲染之前被调用,用于决定组件是否真的需要重新渲染。它接收nextProps和nextState作为参数,可以比较当前的props和state与即将到来的新props和nextState,然后返回一个布尔值来决定是否继续渲染流程。- 而
getSnapshotBeforeUpdate是在render方法之后,componentDidUpdate之前被调用。这个方法用于在组件更新前获取一些信息(例如,DOM元素的滚动位置、尺寸等),然后将这些信息传递给componentDidUpdate,以便在更新后进行相应的处理。
- 在组件更新阶段,
-
示例代码说明
class MyComponent extends React.Component {constructor(props) {super(props);this.state = {count: 0};}shouldComponentUpdate(nextProps, nextState) {console.log('shouldComponentUpdate called');return nextState.count!== this.state.count;}getSnapshotBeforeUpdate(prevProps, prevState) {console.log('getSnapshotBeforeUpdate called');// 假设这里获取某个DOM元素的滚动位置const scrollTop = document.getElementById('myElement').scrollTop;return scrollTop;}componentDidUpdate(prevProps, prevState, snapshot) {console.log('componentDidUpdate called');// 根据获取的快照信息(滚动位置)进行更新后的操作const newScrollTop = snapshot;console.log('Updated scroll top:', newScrollTop);}handleClick = () => {this.setState((prevState) => ({count: prevState.count + 1}));};render() {console.log('render called');return (<div id="myElement" style={{ height: '200px', overflow: 'auto' }}><p>Count: {this.state.count}</p><button onClick={this.handleClick}>Increment</button></div>);} }- 在这个例子中,当点击按钮更新
count状态时:- 首先
shouldComponentUpdate会被调用,检查count状态是否真的发生了变化。如果返回true,则继续更新流程。 - 接着
render方法会被调用,重新生成虚拟DOM。 - 然后
getSnapshotBeforeUpdate被调用,在这里可以获取一些更新前的信息(如scrollTop)并返回。 - 最后
componentDidUpdate会被调用,并且可以使用getSnapshotBeforeUpdate返回的信息进行更新后的操作。
- 首先
- 在这个例子中,当点击按钮更新
React Hook
-
React Hook的种类
- 基础Hook
- useState:用于在函数组件中添加状态。它返回一个数组,其中第一个元素是状态值,第二个元素是更新状态的函数。
- useEffect:用于处理函数组件中的副作用,如数据获取、订阅、手动修改DOM等。它可以模拟类组件中的生命周期方法,如
componentDidMount、componentDidUpdate和componentWillUnmount。 - useContext:用于在函数组件中访问React Context。它使得组件能够订阅React应用中的上下文(Context),并且在上下文的值发生变化时重新渲染。
- 额外的Hook(用于优化等场景)
- useReducer:可以作为
useState的替代方案,用于管理更复杂的状态逻辑。它接受一个reducer函数和一个初始状态作为参数,返回当前状态和一个dispatch函数,用于触发状态更新。 - useCallback:用于优化性能,返回一个记忆化(memoized)的回调函数。只有在依赖项发生变化时,才会重新计算这个回调函数。
- useMemo:用于缓存计算结果,只有在依赖项改变时才会重新计算返回值,避免在每次组件重新渲染时都进行昂贵的计算。
- useRef:返回一个可变的ref对象,其
.current属性被初始化为传入的参数。可以用于访问DOM元素或者在组件的多次渲染之间保存一个可变的值。 - useImperativeHandle:用于在使用
ref时,自定义暴露给父组件的实例值。通常与forwardRef一起使用,来控制组件内部实例的哪些属性或方法可以被外部访问。 - useLayoutEffect:与
useEffect类似,但它会在所有的DOM变更之后同步调用,在浏览器进行绘制之前。可以用于读取DOM布局并同步触发重绘。
- useReducer:可以作为
- 基础Hook
-
开发中常用的React Hook及其应用场景
- useState
- 应用场景:用于在函数组件中存储和更新简单的状态。例如,创建一个计数器组件,或者存储表单输入框中的值。
- 示例代码:
import React, { useState } from 'react'; const Counter = () => {const [count, setCount] = useState(0);const increment = () => {setCount(count + 1);};return (<div><p>Count: {count}</p><button onClick={increment}>Increment</button></div>); }; - useEffect
- 应用场景:
- 数据获取:在组件挂载时获取数据,如从API获取用户列表、文章列表等。
- 事件订阅和取消订阅:订阅外部事件源(如窗口滚动事件、自定义事件等),并在组件卸载时取消订阅,避免内存泄漏。
- 操作DOM(结合
useRef):对DOM元素进行操作,如聚焦输入框、获取元素尺寸等。
- 示例代码(数据获取):
import React, { useEffect, useState } from 'react'; const UserList = () => {const [users, setUsers] = useState([]);useEffect(() => {fetch('https://example.com/api/users').then(response => response.json()).then(data => setUsers(data));}, []);return (<ul>{users.map(user => (<li key={user.id}>{user.name}</li>))}</ul>); }; - 应用场景:
- useContext
- 应用场景:用于在组件树中共享数据,避免层层传递
props。例如,在一个主题切换应用中,共享主题状态(如亮色主题或暗色主题),使得多个组件能够根据主题状态进行渲染。 - 示例代码:
import React, { createContext, useContext, useState } from 'react'; const ThemeContext = createContext(); const ThemeProvider = ({ children }) => {const [theme, setTheme] = useState('light');return (<ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>); }; const Button = () => {const { theme, setTheme } = useContext(ThemeContext);const toggleTheme = () => {setTheme(theme === 'light'? 'dark' : 'light');};return (<button onClick={toggleTheme}>{theme === 'light'? 'Switch to Dark Theme' : 'Switch to Light Theme'}</button>); }; const App = () => {return (<ThemeProvider><Button /></ThemeProvider>); }; - 应用场景:用于在组件树中共享数据,避免层层传递
- useReducer
- 应用场景:当组件的状态更新逻辑比较复杂,涉及多个子状态或者有复杂的状态转换规则时使用。例如,在一个购物车组件中,管理购物车中商品的添加、删除、数量修改等操作,状态更新逻辑可以通过reducer函数来统一管理。
- 示例代码:
import React, { useReducer } from 'react'; const initialState = {cart: [] }; const reducer = (state, action) => {switch (action.type) {case 'ADD_TO_CART':return {...state,cart: [...state.cart, action.payload]};case 'REMOVE_FROM_CART':return {...state,cart: state.cart.filter(item => item.id!== action.payload.id)};default:return state;} }; const ShoppingCart = () => {const [state, dispatch] = useReducer(reducer, initialState);const addToCart = (product) => {dispatch({ type: 'ADD_TO_CART', payload: product });};const removeFromCart = (product) => {dispatch({ type: 'REMOVE_FROM_CART', payload: product });};return (<div><button onClick={() => addToCart({ id: 1, name: 'Product 1' })}>Add to Cart</button><ul>{state.cart.map(item => (<li key={item.id}>{item.name}<button onClick={() => removeFromCart(item)}>Remove</button></li>))}</ul></div>); }; - useCallback
- 应用场景:用于优化性能,当把一个回调函数作为
props传递给子组件,并且这个回调函数在组件的多次渲染过程中不应该被重新创建(除非其依赖项发生变化)时使用。例如,在一个包含大量子组件的列表中,父组件有一个删除按钮的回调函数,使用useCallback可以避免不必要的子组件重新渲染。 - 示例代码:
import React, { useState, useCallback } from 'react'; const ParentComponent = () => {const [count, setCount] = useState(0);const handleDelete = useCallback(() => {// 执行删除操作console.log('Delete item');}, []);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment</button><ChildComponent onDelete={handleDelete} /></div>); }; const ChildComponent = ({ onDelete }) => {return (<button onClick={onDelete}>Delete</button>); }; - 应用场景:用于优化性能,当把一个回调函数作为
- useMemo
- 应用场景:用于缓存计算结果,当组件中有一些昂贵的计算(如复杂的数据转换、大量数据的过滤等),并且这些计算结果在依赖项没有改变的情况下不需要重新计算时使用。例如,在一个数据表格组件中,对表格数据进行排序和过滤的计算可以使用
useMemo进行缓存。 - 示例代码:
import React, { useState, useMemo } from 'react'; const DataTable = () => {const [data, setData] = useState([{ id: 1, name: 'John', age: 30 },{ id: 2, name: 'Alice', age: 25 },{ id: 3, name: 'Bob', age: 35 }]);const [sortBy, setSortBy] = useState('name');const sortedData = useMemo(() => {if (sortBy === 'name') {return data.sort((a, b) => a.name.localeCompare(b.name));} else if (sortBy === 'age') {return data.sort((a, b) => a.age - b.age);}return data;}, [data, sortBy]);return (<div><select onChange={(e) => setSortBy(e.target.value)}><option value="name">Sort by Name</option><option value="age">Sort by Age</option></select><table><thead><tr><th>ID</th><th>Name</th><th>Age</th></tr></thead><tbody>{sortedData.map(item => (<tr key={item.id}><td>{item.id}</td><td>{item.name}</td><td>{item.age}</td></tr>))}</tbody></table></div>); }; - 应用场景:用于缓存计算结果,当组件中有一些昂贵的计算(如复杂的数据转换、大量数据的过滤等),并且这些计算结果在依赖项没有改变的情况下不需要重新计算时使用。例如,在一个数据表格组件中,对表格数据进行排序和过滤的计算可以使用
- useState
useState 和 useReducer 的区别
- 状态管理的复杂度
- useState
- 简单状态管理:
useState主要用于管理简单的、独立的状态。例如,一个计数器组件中的计数状态,或者一个输入框组件中的输入值状态。 - 示例代码(计数器):
import React, { useState } from 'react'; const Counter = () => {const [count, setCount] = useState(0);const increment = () => {setCount(count + 1);};return (<div><p>Count: {count}</p><button onClick={increment}>Increment</button></div>); }; - 简单状态管理:
- useReducer
- 复杂状态管理:
useReducer适用于处理更复杂的状态逻辑,特别是当状态的更新依赖于前一个状态,并且涉及多种不同的操作类型时。例如,在一个表单组件中,管理表单的提交状态、验证状态以及字段值状态等多个相关状态。 - 示例代码(简单的表单验证):
import React, { useReducer } from 'react'; const initialState = {value: '',isTouched: false,isError: false }; const reducer = (state, action) => {switch (action.type) {case 'INPUT_CHANGE':return {...state,value: action.payload,isError: false};case 'INPUT_BLUR':return {...state,isTouched: true,isError: state.value.trim() === ''};default:return state;} }; const InputForm = () => {const [state, dispatch] = useReducer(reducer, initialState);const { value, isTouched, isError } = state;const onChangeHandler = (e) => {dispatch({ type: 'INPUT_CHANGE', payload: e.target.value });};const onBlurHandler = () => {dispatch({ type: 'INPUT_BLUR' });};return (<div><inputtype="text"value={value}onChange={onChangeHandler}onBlur={onBlurHandler}/>{isTouched && isError && <p>Input must not be empty.</p>}</div>); }; - 复杂状态管理:
- useState
- 状态更新方式
- useState
- 直接更新:通过调用
setState函数(useState返回的第二个元素)来更新状态。这个函数可以接收新的状态值作为参数,React会自动将新值与旧值进行合并(对于对象类型的状态),或者直接替换(对于基本类型的状态)。 - 示例(更新对象状态):
import React, { useState } from 'react'; const UserProfile = () => {const [user, setUser] = useState({name: 'John',age: 30});const updateName = () => {setUser({...user,name: 'Alice'});};return (<div><p>Name: {user.name}</p><p>Age: {user.age}</p><button onClick={updateName}>Update Name</button></div>); }; - 直接更新:通过调用
- useReducer
- 基于动作(Action)更新:
useReducer返回一个包含当前状态和dispatch函数的数组。通过dispatch函数发送一个动作(action)来触发状态更新。action是一个包含type属性(通常是一个字符串,用于标识动作类型)和可选的payload属性(用于传递与动作相关的数据)的对象。reducer函数根据接收到的action类型来决定如何更新状态。 - 示例(计数器的另一种实现方式):
import React, { useReducer } from 'react'; const initialState = 0; const reducer = (state, action) => {switch (action.type) {case 'INCREMENT':return state + 1;case 'DECREMENT':return state - 1;default:return state;} }; const Counter = () => {const [count, dispatch] = useReducer(reducer, initialState);const increment = () => {dispatch({ type: 'INCREMENT' });};const decrement = () => {dispatch({ type: 'DECREMENT' });};return (<div><p>Count: {count}</p><button onClick={increment}>Increment</button><button onClick={decrement}>Decrement</button></div>); }; - 基于动作(Action)更新:
- useState
- 可预测性和调试便利性
- useState
- 简单但可能复杂的更新逻辑:对于简单的状态更新,
useState很直观。但当状态更新逻辑变得复杂,涉及多个依赖于旧状态的操作时,可能会导致代码难以理解和调试。例如,在一个复杂的异步操作后更新多个状态时,很难追踪每个状态更新的顺序和原因。
- 简单但可能复杂的更新逻辑:对于简单的状态更新,
- useReducer
- 更具可预测性的更新流程:
useReducer的更新逻辑基于reducer函数,它是一个纯函数,对于给定的当前状态和action,总是返回相同的新状态。这种确定性使得状态更新的流程更加清晰,便于调试。在大型应用或复杂的组件中,当状态更新依赖于多种条件和操作时,useReducer有助于保持代码的可维护性和可预测性。
- 更具可预测性的更新流程:
- useState
总结 (react 主要生命周期,常用生命周期,不常用生命周期,特殊环境下的生命周期)
-
主要生命周期(函数组件和类组件都涉及或有替代方式)
- 挂载(Mounting)阶段
- constructor(类组件):用于初始化组件的状态(
this.state)和绑定事件处理函数。在组件创建时首先被调用,必须先调用super(props)。 - render:在挂载和更新阶段都会被调用,是React组件中唯一必需的方法。用于描述组件的UI结构,返回一个React元素,应该是一个纯函数。
- componentDidMount(类组件)/ useEffect(函数组件):在组件挂载到DOM后立即被调用(
useEffect可以通过空依赖数组模拟)。适合进行需要DOM节点的操作,如发送网络请求、初始化第三方库等。
- constructor(类组件):用于初始化组件的状态(
- 更新(Updating)阶段
- shouldComponentUpdate(类组件)/ React.memo(函数组件):用于性能优化,决定组件是否需要重新渲染。
shouldComponentUpdate在类组件接收新的props或者state更新之前被调用,React.memo是一个高阶组件,用于对函数组件的props进行浅比较来决定是否重新渲染。 - render(同挂载阶段):更新阶段也会调用
render来生成新的虚拟DOM。 - componentDidUpdate(类组件)/ useEffect(函数组件):在组件更新后被调用。可以在这里操作DOM,根据更新前后的
props和state进行一些额外的操作,useEffect通过依赖数组来控制在特定状态或属性变化时执行副作用。
- shouldComponentUpdate(类组件)/ React.memo(函数组件):用于性能优化,决定组件是否需要重新渲染。
- 卸载(Unmounting)阶段
- componentWillUnmount(类组件)/ useEffect(函数组件返回清理函数):在组件从DOM中移除之前被调用。用于清理在
componentDidMount或其他生命周期中创建的副作用,如清除定时器、取消网络请求、取消订阅等。
- componentWillUnmount(类组件)/ useEffect(函数组件返回清理函数):在组件从DOM中移除之前被调用。用于清理在
- 挂载(Mounting)阶段
-
常用生命周期(主要是在类组件中)
- componentDidMount:
- 应用场景广泛,几乎所有需要在组件加载后进行的操作都会用到。例如,在组件挂载后发送网络请求获取数据来填充组件内容。
class DataFetcher extends React.Component {constructor(props) {super(props);this.state = { data: null };}componentDidMount() {fetch('https://example.com/api/data').then(response => response.json()).then(data => this.setState({ data }));}render() {return (<div>{this.state.data? (<ul>{this.state.data.map(item => (<li key={item.id}>{item.name}</li>))}</ul>) : (<p>Loading...</p>)}</div>);} } - shouldComponentUpdate:
- 用于性能优化,当组件重新渲染成本较高或者需要避免不必要的渲染时使用。例如,一个组件只有在特定
props变化时才需要重新渲染。
class MyComponent extends React.Component {shouldComponentUpdate(nextProps, nextState) {return nextProps.importantValue!== this.props.importantValue;}render() {return <div>{this.props.importantValue}</div>;} } - 用于性能优化,当组件重新渲染成本较高或者需要避免不必要的渲染时使用。例如,一个组件只有在特定
- componentDidUpdate:
- 当组件更新后需要进行一些额外操作时使用。例如,根据组件更新后的状态来更新第三方库的配置。
class ChartComponent extends React.Component {constructor(props) {super(props);this.state = { data: [] };}componentDidMount() {// 初始数据加载和图表初始化this.fetchDataAndUpdateChart();}componentDidUpdate(prevProps, prevState) {if (prevState.data!== this.state.data) {// 数据变化后更新图表this.updateChartWithNewData();}}fetchDataAndUpdateChart = () => {fetch('https://example.com/api/chart-data').then(response => response.json()).then(data => this.setState({ data }));};updateChartWithNewData = () => {// 使用this.state.data更新图表的逻辑};render() {return <div id="chart-container"></div>;} }
- componentDidMount:
-
不常用生命周期(类组件)
- componentWillMount(已废弃):
- 因为可能会导致一些难以预测的副作用,并且在异步渲染等场景下可能会出现问题,在React 16.3版本后被标记为不安全的生命周期方法,不建议使用。它在组件挂载之前被调用,且在
render方法之前。
- 因为可能会导致一些难以预测的副作用,并且在异步渲染等场景下可能会出现问题,在React 16.3版本后被标记为不安全的生命周期方法,不建议使用。它在组件挂载之前被调用,且在
- componentWillReceiveProps(已废弃):
- 会导致性能问题和意外的行为,尤其是在父组件频繁更新传递
props的情况下。从React 16.3版本后被标记为不安全的生命周期方法,不建议使用。它在组件接收到新的props时被调用,在render方法之前。
- 会导致性能问题和意外的行为,尤其是在父组件频繁更新传递
- componentWillUpdate(已废弃):
- 同样可能导致难以预测的副作用,在异步渲染场景下会出现问题。React 16.3版本后被标记为不安全的生命周期方法,不建议使用。在组件更新之前被调用,在
shouldComponentUpdate之后,render之前。
- 同样可能导致难以预测的副作用,在异步渲染场景下会出现问题。React 16.3版本后被标记为不安全的生命周期方法,不建议使用。在组件更新之前被调用,在
- componentWillMount(已废弃):
-
特殊环境下的生命周期(类组件)
- getDerivedStateFromProps:
- 应用场景包括根据
props初始化或更新state,实现受控组件和非受控组件之间的转换等。它是一个静态方法,在组件实例化之后、render方法之前调用,并且在组件每次接收到新的props时也会被调用。
class InputWrapper extends React.Component {static getDerivedStateFromProps(props, state) {if ('value' in props) {return {isControlled: true,value: props.value};}return {isControlled: false,value: state.value};}constructor(props) {super(props);this.state = {value: '',isControlled: false};}handleChange = (e) => {if (!this.state.isControlled) {this.setState({value: e.target.value});}};render() {return (<inputtype="text"value={this.state.isControlled? this.state.value : undefined}onChange={this.handleChange}/>);} } - 应用场景包括根据
- getSnapshotBeforeUpdate:
- 在组件更新阶段被调用,具体是在
render方法之后,componentDidUpdate之前。用于获取组件更新前的最后一个“快照”(如DOM状态或其他相关信息),并将其传递给componentDidUpdate。例如,用于保存滚动位置或记录元素尺寸变化等场景。
class ScrollableList extends React.Component {constructor(props) {super(props);this.listRef = React.createRef();this.state = {items: [],scrollTop: 0};}componentDidMount() {// 模拟获取初始数据this.setState({items: [1, 2, 3, 4, 5]});}handleAddItem = () => {const newItems = [...this.state.items, this.state.items.length + 1];this.setState({items: newItems});}getSnapshotBeforeUpdate(prevProps, prevState) {if (this.listRef.current) {return this.listRef.current.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {if (snapshot!== null) {this.listRef.current.scrollTop = snapshot;}}render() {return (<div ref={this.listRef} style={{ height: '200px', overflow: 'auto' }}><ul>{this.state.items.map((item) => (<li key={item}>{item}</li>))}</ul><button onClick={this.handleAddItem}>Add Item</button></div>);} }
- 在组件更新阶段被调用,具体是在
- getDerivedStateFromProps:

相关文章:
react 总结+复习+应用加深
文章目录 一、React生命周期1. 挂载阶段(Mounting)补充2. 更新阶段(Updating)补充 static getDerivedStateFromProps 更新阶段应用补充 getSnapshotBeforeUpdate3. 卸载阶段(Unmounting) 二、React组件间的…...
关于 API
关于 API $set 问法:有没有遇到过数据更新了但视图没有更新的情况? <template><div>{{arr}}<button click"btn"></button></div> </template><script> export default {name:"Home"da…...
第15次CCF CSP真题解
1、小明上学 题目链接:https://sim.csp.thusaac.com/contest/15/problem/0 本题是模拟红绿灯计时的题,根据红绿灯转换规则可知,红灯后面通常是绿灯,绿灯后面是黄灯,黄灯过后又是红灯。根据题意,当k 0时&…...
STM32硬件平台
STM32 系列是 STMicroelectronics 设计的高度灵活、广泛应用的微控制器(MCU)系列,支持从低功耗应用到高性能处理的需求,适用于工业、汽车、消费电子和物联网等广泛领域。STM32 系列具有广泛的硬件种类和丰富的功能,以下…...
一文讲明白大模型分布式逻辑(从GPU通信原语到Megatron、Deepspeed)
1. 背景介绍 如果你拿到了两台8卡A100的机器(做梦),你的导师让你学习部署并且训练不同尺寸的大模型,并且写一个说明文档。你意识到,你最需要学习的就是关于分布式训练的知识,因为你可是第一次接触这么多卡…...
【人工智能-初级】第6章 决策树和随机森林:浅显易懂的介绍及Python实践
文章目录 一、决策树简介二、决策树的构建原理2.1 决策树的优缺点优点缺点 三、随机森林简介3.1 随机森林的构建过程3.2 随机森林的优缺点优点缺点 四、Python实现决策树和随机森林4.1 导入必要的库4.2 加载数据集并进行预处理4.3 创建决策树模型并进行训练4.4 可视化决策树4.5…...
时间序列预测(九)——门控循环单元网络(GRU)
目录 一、GRU结构 二、GRU核心思想 1、更新门(Update Gate):决定了当前时刻隐藏状态中旧状态和新候选状态的混合比例。 2、重置门(Reset Gate):用于控制前一时刻隐藏状态对当前候选隐藏状态的影响程度。…...
李东生牵手通力股份IPO注册卡关,三年近10亿“清仓式分红”引关注
《港湾商业观察》施子夫 9月27日,通力科技股份有限公司(以下简称,通力股份)再度提交了注册申请,实际上早在去年11月6日公司已经提交过注册,看起来公司注册环节面临卡关。公开信息显示,通力股份…...
Android13、14特殊权限-应用安装权限适配
Android13、14特殊权限-应用安装权限适配 文章目录 Android13、14特殊权限-应用安装权限适配一、前言二、权限适配三、其他1、特殊权限-应用安装权限适配小结2、dumpsys package查看获取到了应用安装权限3、Android权限系统:应用操作管理类AppOpsManager(…...
DMVPN协议
DMVPN(Dynamic Multipoint VPN)动态多点VPN 对于分公司和分总公司内网实现通信环境下,分公司是很多的。我们不可能每个分公司和总公司都挨个建立ipsec隧道 ,而且如果是分公司和分公司建立隧道,就会很麻烦。此时我们需…...
leetcode动态规划(十八)-零钱兑换II
题目 322.零钱兑换II 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬…...
2024 CSP-J 题解
2024 CSP-J题解 扑克牌 题目给出了一整套牌的定义,但是纯粹在扯淡,完全没有必要去判断给出的牌的花色和点数,我们用一个循环来依次读入每一张牌,如果这个牌在之前出现过,我们就让答案减一。这里建议用map、unorde…...
GPU 服务器厂家:中国加速计算服务器市场的前瞻洞察
科技的飞速发展,让 GPU 服务器在加速计算服务器领域的地位愈发凸显。中国加速计算服务器市场正展现出蓬勃的生机,而 GPU 服务器厂家则是这场科技盛宴中的关键角色。 从市场预测的趋势来看,2023 年起,中国加速计算服务器市场便已展…...
Hadoop集群修改yarn队列
1.修改默认的default队列参数 注意: yarn.scheduler.capacity.root.队列名.capacity总和不能超过100 <property><name>yarn.scheduler.capacity.root.queues</name><value>default,hive,spark,flink</value><description>The…...
【GPIO】2.ADC配置错误,还是能得到电压数据
配置ADC功能时,GPIO引脚弄错了,P1写成P2,但还是配置成功,能得到电压数据。 首先一步步排查: 既然引脚弄错了,那引脚改为正确的引脚,能得到数据通过第一步判断,GPIO配置似乎是不起作…...
css-元素居中方式
<section class"wrapper"><div class"content">Content goes here</div> </section>1. 使用 Flexbox Flexbox 是一种现代的布局方法,可以轻松实现居中。 .wrapper {display: flex; /* 使用 Flexbox …...
redis内存打满了怎么办?
1、设置maxmemory的大小 我们需要给 Redis设置maxmemory的大小,如果不设置的话,它会受限于系统的物理内存和系统对内存的管理机制。 2、设置内存的淘汰策略 内存的淘汰策略分为 8 种,从淘汰范围来说分为从所有的key中淘汰和从设置过期时间…...
决策算法的技术分析
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言(1)第一层级:分层状态机、分层决策树的想法(三个臭皮匠胜过一个诸葛亮)基于场景的固定规则化的分层决策核心思想(2)第二层级:数据管理的方…...
【Python爬虫】获取汽车之家车型配置附代码(2024.10)
参考大哥,感谢大哥:https://blog.csdn.net/weixin_43498642/article/details/136896338 【任务目标】 工作需要想更方便地下载汽车之家某车系配置清单;(垃圾汽车之家不给下载导出表格,配置页叉掉了车系要出来还要重新…...
JVM 加载 class 文件的原理机制
JVM 加载 class 文件的原理机制 JVM(Java虚拟机)是一个可以执行Java字节码的虚拟机。它负责执行Java应用程序和应用程序的扩展,如Java库和框架。 文章目录 JVM 加载 class 文件的原理机制1. JVM1.1 类加载器1.2 魔数1.3 元空间 2. 类加载2.1 …...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
