Redux store变更时React组件未重渲染,forceUpdate也无效如何解决?
问题根因
- Reducer 违反 Redux 不可变更新原则,直接修改原 state 中的对象属性,且未返回新的
games数组引用,Redux 无法检测到状态变更 - 组件
connect时未传入mapStateToProps,没有订阅 Redux store 中的games状态变更,store 变化时组件不会收到新的 props - 组件内部用
useState对传入的games做了本地缓存,仅在组件初始化时赋值一次,后续 props 中的games变更不会同步到本地 state - 强制更新逻辑属于无效操作,问题根源不在渲染触发,而在数据源没有同步更新
修复方案
1. 修复 Reducer 逻辑
所有状态变更都要返回新的对象/数组引用,禁止直接修改原 state 或传入的 payload 数据:
import { SET_GAMES, SET_GAME_VISIBLE, SET_GAME_INVISIBLE } from '../actions/games'; export const initialState = { games: [], }; export default function games(state = initialState, action) { switch(action.type) { case SET_GAMES: { // 生成新数组,不修改原payload数据 const newGames = action.payload.games.map(obj => ({ ...obj, isVisible: true })) return { ...state, games: newGames, }; } case SET_GAME_VISIBLE: { // 返回新数组,修改的项生成新对象避免修改原state return { ...state, games: state.games.map(obj => { if (obj.id === action.payload.id) { return {...obj, isVisible: true} } return obj }) }; } case SET_GAME_INVISIBLE: { return { ...state, games: state.games.map(obj => { if (obj.id === action.payload.id) { return {...obj, isVisible: false} } return obj }) }; } default: return state; } }
2. 修复组件逻辑
补充 mapStateToProps 订阅 Redux 状态,增加 useEffect 同步 Redux 数据到本地状态,删除无用的强制更新代码:
import React, { useState, useEffect } from 'react'; import { ReactSortable } from 'react-sortablejs'; import ListItem from '@mui/material/ListItem'; import Checkbox from '@mui/material/Checkbox'; import { connect } from 'react-redux'; import { setGameVisible, setGameInvisible, setGames } from '../store/actions/games'; interface ItemType { id: string; name: string; isVisible: boolean; } interface IGamesListProps { games: ItemType[]; setGameVisible: (id: string) => void; setGameInvisible: (id: string) => void; setGames: (games: ItemType[]) => void; } const GamesList: React.FunctionComponent<IGamesListProps> = ({games, setGameVisible, setGameInvisible, setGames}) => { const [state, setState] = useState<ItemType[]>(games); // 监听Redux的games变化,同步到本地state保证数据一致性 useEffect(() => { setState(games) }, [games]) const onCheckboxChangeHandle = (id: string, isVisible: boolean) => { isVisible ? setGameInvisible(id) : setGameVisible(id); } // 排序时如果需要持久化到Redux,调用对应action,不需要可以只保留setState const handleSortChange = (newList: ItemType[]) => { setState(newList) setGames(newList) } return ( <ReactSortable list={state} setList={handleSortChange} tag='ul'> {state.map((item) => ( <ListItem sx={{ maxWidth: '300px' }} key={item.id} secondaryAction={ <Checkbox edge="end" onChange={() => onCheckboxChangeHandle(item.id, item.isVisible)} checked={item.isVisible} /> } > {item.name} </ListItem> ))} </ReactSortable> ); }; // 订阅Redux中的games状态,映射到组件props const mapStateToProps = (state: any) => ({ games: state.games.games }) export default connect(mapStateToProps, { setGameVisible, setGameInvisible, setGames })(GamesList);
如果不需要把排序结果同步到 Redux,删除 setGames 相关逻辑即可。
内容的提问来源于stack exchange,提问作者Danton
相关产品推荐
相关产品推荐

