React中getDerivedStateFromProps同步props与含搜索栏组件state异常
问题诊断
同步失效是两个核心逻辑错误导致的:
getDerivedStateFromProps判断逻辑完全错误:当前代码对比的是props.data和state.data,但state.data会被搜索功能修改为过滤后的结果集,只要用户执行过搜索,二者永远不相等,会导致每次渲染都直接用props覆盖搜索结果;同时你没有留存上一次接收的props数据引用,根本无法区分数据变化是来自Redux的props更新,还是本地搜索修改了state。- 原始数据备份
arrayholder仅在构造函数中赋值一次,后续Redux推送新的props.data时,这个备份变量从未同步更新,搜索逻辑永远基于初始加载的老数据过滤。 - 额外提个性能问题:你把
Item组件定义在render方法内部,每次渲染都会重新生成全新的组件引用,React会判定组件类型变化,触发不必要的卸载重挂载,浪费性能。
修复方案
在state中单独新增字段存储上一次从props接收的原始数据引用,作为props是否真的更新的判断依据,不要直接拿props和可能被本地修改过的展示用state做对比。当检测到props.data真实更新时,同步更新state中的展示数据、标记位,同时更新实例上的原始数据备份,保证搜索逻辑使用最新的原始数据源。
如果需要props更新后保留用户当前输入的搜索关键词,在数据更新后重新执行一次过滤逻辑即可;不需要的话直接清空搜索状态。
修正后的核心代码如下:
// 将Item组件移到类外部定义,避免render内重复创建 const Item = ({ nom_shotgun, cle, photo_shotgun, date_shotgun, etat, admin, onLongPress, onPress, renderAdmin }) => { const handlerLongClick = () => { Alert.alert( "Alert", "Supprimer le shotgun?", [ { text: "oui", onPress: () => onLongPress(cle) }, { text: "non", onPress: () => console.log("no Pressed") } ] ); }; return ( <TouchableOpacity style={styles.item} onPress={onPress} onLongPress={handlerLongClick}> <View> <Text style={styles.nom_shotgun}>{nom_shotgun}</Text> <Text style={styles.date_shotgun}>{date_shotgun}</Text> {renderAdmin(admin)} <Text style={styles.etat}> {etat} </Text> <Image style={styles.photo_shotgun} source={require('../image/shotgun2.png')} /> <Divider style={{ height: 1 }} /> </View> </TouchableOpacity> ) } class modification extends React.Component { constructor(props) { super(props) this.state = { cle: props.cle, nom_shotgun: props.nom_shotgun, date_shotgun: props.date_shotgun, photo_shotgun: props.photo_shotgun, etat: props.etat, admin: props.admin, error: null, searchValue: "", data: props.data, // 存储上一次从props接收的原始数据,用于更新判断 lastPropsData: props.data, loading: false, }; // 初始化原始全量数据备份 this.arrayholder = props.data; } searchFunction = (text) => { const updatedData = this.arrayholder.filter((item) => { const item_data = `${item.nom_shotgun.toUpperCase()})`; const text_data = text.toUpperCase(); return item_data.indexOf(text_data) > -1; }); this.setState({ data: updatedData, searchValue: text }); }; static getDerivedStateFromProps(props, state) { // 仅对比当前props数据和上次存储的props快照,不和本地修改过的展示数据对比 if (props.data !== state.lastPropsData) { return { lastPropsData: props.data, data: props.data, // 不需要props更新后保留搜索内容就保留这行,需要就删掉 searchValue: "" }; } return null } componentDidUpdate(prevProps) { // getDerivedStateFromProps是静态方法无法访问实例this,在这里同步更新实例上的全量数据备份 if (prevProps.data !== this.props.data) { this.arrayholder = this.props.data; // 如果需要保留当前搜索关键词,打开下方注释,用新的全量数据重新执行过滤 // if (this.state.searchValue) { // this.searchFunction(this.state.searchValue) // } } } _supprimer = (cle) => { // 原有删除逻辑 } _navigation = (etat, admin) => { // 原有页面跳转逻辑 } _admin = (admin) => { // 原有管理员状态渲染逻辑 } render() { const { etat, admin } = this.props; return ( <View> {/* 搜索输入框绑定searchFunction即可 */} {/* 列表渲染注意传入正确参数,示例: <FlatList data={this.state.data} renderItem={({ item }) => ( <Item nom_shotgun={item.nom_shotgun} cle={item.cle} photo_shotgun={item.photo_shotgun} date_shotgun={item.date_shotgun} etat={item.etat} admin={item.admin} onLongPress={this._supprimer} onPress={() => this._navigation(etat, admin)} renderAdmin={this._admin} /> )} keyExtractor={item => item.cle} /> */} </View> ); }; }
几个注意点:
- 用独立字段存props快照是React官方推荐的
getDerivedStateFromProps派生状态受控场景的标准写法,完全避开了生命周期废弃的问题,也能正确区分props更新和本地state修改。 - 原来render里定义的Item组件没有返回JSX结构,事件方法也没有正确绑定实例this,移到外部后通过props传入回调即可解决this指向问题。
- 如果你的数据是数组,尽量保证Redux更新数据时返回新的数组引用,不然引用不变的话
!==判断会识别不到更新,这也是Redux immutable更新的基本要求。
内容的提问来源于stack exchange,提问作者ZHOU Alec
相关产品推荐
相关产品推荐

