useContextState Hook状态重置问题求助:组件重渲染时状态丢失
问题:自定义useContextState Hook组件重渲染时状态重置
我使用了一个自定义useContextState Hook,但组件重渲染时Context状态会被重置,无法持久化。以下是相关代码:
自定义useContextState Hook代码
import React from 'react'; const Context = React.createContext([{}, () => {}]); const Provider = ({ children }) => { const state = React.useState({}); return <Context.Provider value={state}>{children}</Context.Provider>; }; export const withContextProvider = (Component) => (props) => ( <Provider> <Component {...props} /> </Provider> ); export function useContextState(contextKey, initialState) { const [contextState, setContextState] = React.useContext(Context); const state = contextState[contextKey] != null ? contextState[contextKey] : initialState; const setState = (nextState) => setContextState((prevState) => Object.assign({}, prevState, { [contextKey]: typeof nextState === 'function' ? nextState(prevState) : nextState, }) ); React.useLayoutEffect(() => { if (contextState[contextKey] == null && state != null) { setContextState((prevState) => { if (prevState[contextKey] == null) { return Object.assign({}, prevState, { [contextKey]: state, }); } return prevState; }); } }, [contextKey]); return [state, setState]; }
使用方式
自定义useValidation Hook
const useValidation = () => { const [validationState, setValidationState] = useContextState('ValidationState', null); const validate = () => { // 异步执行验证操作 setValidationState(validationResFromServer); } return [validationState, validate]; }
App组件使用
function App(){ const [validationState, validate] = useValidation(); const validationButtonClick = () => validate(); const navigate = () => { /* 触发App组件重渲染的函数 */ } } export default withContextProvider(App);
问题原因
- Provider重复创建:
withContextProvider高阶组件每次在业务组件重渲染时,都会创建一个新的Provider实例。而Provider内部的useState({})会在新实例初始化时重置为初始空对象,导致Context状态丢失。 - Effect依赖缺失:
useContextState中的useLayoutEffect只传入了contextKey作为依赖,没有包含contextState和state,这会导致effect执行时机错误,可能引发额外的状态覆盖问题。
修复方案
1. 确保Provider仅初始化一次
修改withContextProvider,避免随业务组件重渲染重复创建Provider:
import React, { memo } from 'react'; const Context = React.createContext([{}, () => {}]); // 用memo包裹Provider,避免无意义重渲染 const Provider = memo(({ children }) => { const state = React.useState({}); return <Context.Provider value={state}>{children}</Context.Provider>; }); export const withContextProvider = (Component) => { // 将Provider包裹逻辑移出渲染函数,确保仅创建一次 const WrappedComponent = (props) => ( <Provider> <Component {...props} /> </Provider> ); WrappedComponent.displayName = `WithContextProvider(${Component.displayName || Component.name})`; return WrappedComponent; };
2. 完善Effect依赖项
补全useLayoutEffect的依赖数组,确保状态变化时正确触发:
React.useLayoutEffect(() => { if (contextState[contextKey] == null && state != null) { setContextState((prevState) => { if (prevState[contextKey] == null) { return Object.assign({}, prevState, { [contextKey]: state, }); } return prevState; }); } }, [contextKey, contextState, state]); // 补充缺失的依赖
3. 更优的Provider使用方式
推荐直接在应用顶层(如index.js)挂载Provider,彻底避免高阶组件带来的重复创建问题:
// index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from './useContextState'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider> <App /> </Provider> );
此时App组件无需再用withContextProvider包裹,直接导出即可。
内容的提问来源于stack exchange,提问作者bhushan laware
相关产品推荐
相关产品推荐

