React-Redux中useDispatch更新状态后useSelector未触发组件更新
问题原因
两个错误直接导致useSelector订阅失效,和不可变更新、Immer逻辑无关:
- 取数路径完全错误。你在configureStore里把pagesReducer挂载到了
pagesInfo字段下,也就是说slice里的currentElementId、currentPage、pages全在state.pagesInfo命名空间下,根state没有直接暴露这些属性。你代码里所有useSelector(state => state.currentElementId)取到的永远是undefined,根本没有订阅到实际存储值的状态节点,useSelector做===比较时永远拿到undefined,判定状态没变化,自然不会触发重渲染。 - 违反React Hooks调用规则。你在App组件内嵌套的普通函数
GetPageContent里调用了useSelector,Hooks只能在函数组件顶层、自定义Hook顶层调用,写在嵌套普通函数里会导致Hook调用顺序错乱,订阅逻辑完全不生效。
你在Redux DevTools里能看到值更新是正常的,DevTools直接读取store全量状态,不受组件侧订阅逻辑错误的影响,之前尝试手动返回新状态对象没用,因为问题根本不在状态更新环节。
修复方法
- 修正所有useSelector的取数路径,所有slice状态都从
state.pagesInfo下读取,不要直接从根state取slice内部字段。 - 把所有嵌套在普通函数、条件判断、循环里的useSelector移到App组件顶层,严格遵守Hooks调用规则。
修正后的核心代码结构如下:
function App(props) { // 所有Hook统一放组件最顶层,路径修正正确 const dispatch = useDispatch(); const currentElementId = useSelector(state => state.pagesInfo.currentElementId); const currentPage = useSelector(state => state.pagesInfo.currentPage); const rawPageLinesContent = useSelector(state => state.pagesInfo.pages[currentPage].lines); console.log(currentElementId); const changeElementID = event => { const target = event.target.closest("li") if (target) { const elementID = target.getAttribute("elementid"); dispatch(changeCurrentElementID(elementID)); } } // 内部渲染函数不再调用Hook,直接使用顶层取到的状态 const renderPageContent = () => { return rawPageLinesContent.map(line => { return ( <ul key={line.id} className={css["page-line"]}> { line.content.map(element => { let finalElement; if (element.type == "symbol") { const style = { fontSize: element.fontSize + "px" } finalElement = ( <li onClick={changeElementID} key={element.id} elementid={element.id} className={`${css["page-line__item"]} ${css["page-line__symbol"]}`} style={style}>{element.symbol}</li> ) if (currentElementId == element.id) { finalElement = ( <input key={element.id} elementid={element.id} maxLength="1" className={`${css["page-line__item"]} ${css["page-line__symbol"]} ${css["page-line__choosen"]}`} style={style} value={element.symbol} /> ) } } else if (element.type == "image") { finalElement = ( <li onClick={changeElementID} key={element.id} elementid={element.id} className={`${css["page-line__item"]} ${css["page-line__image"]}`}> <img src={element.imageSrc} width={element.width} height={element.height} alt="image" /> </li> ) } return finalElement; }) } </ul > ) }) } return ( <div className={css["App"]}> <header className={`${css['App__header']} ${css['tools']} `}> <EnterImageButton /> <TextSizing /> <TextDecoration /> <TextAligning /> <div>{currentElementId || 0}</div> </header > <div className={`${css['App__content']} ${css['pages']} `}> <div className={`${css['pages__item']}`} > {renderPageContent()} </div> </div> </div > ); }
内容的提问来源于stack exchange,提问作者Aogava
相关产品推荐
相关产品推荐

