向组件传递状态及状态修改器的最佳实践
如何简化React组件中大量传递的Props?
我正在重构一个接收大量props的组件,想参考高级React模式优化。针对如下代码,希望减少props数量或对其分组,有没有能把关联状态和状态修改器分组的模式?
const App = () => { const [ab, setAb] = useState(); const [c, setC] = useState(); const [d, setD] = useState(); // stuff with all available state return (<Example ab={ab} setAb={setAb} c={c} setC={setC} d={d} setD={setD} />) } const Example = ({ab, setAb, c, setC, d, setD}) => { const [a, setA] = useState(); // stuff with all available vars return ( <> <A ab={ab} setAb={setAb} a={a} setA={setA}/> <B ab={ab} setAb={setAb}/> <C c={c} setC={setC} d={d} setD={setD}/> </> ) } const A = ({ab, setAb, a, setA}) => { // stuff with all available vars return <div>{a}</div> } const B = ({ab, setAb}) => { // stuff with setAb return <div>{ab}</div> } const C = ({c, setC, d, setD}) => { // stuff with all available vars return (<D d={d} setD={setD} />) } const D = ({d, setD}) => { // stuff with setAb return (<div>{d}</div>); }
方案1:关联状态与修改器分组传递
把逻辑相关的状态和setter打包成对象传递,既减少单个props的数量,又能保持状态的关联性:
修改后代码示例
const App = () => { const [ab, setAb] = useState(); const [c, setC] = useState(); const [d, setD] = useState(); // 分组关联状态 const abState = { value: ab, setValue: setAb }; const cState = { value: c, setValue: setC }; const dState = { value: d, setValue: setD }; return (<Example abState={abState} cState={cState} dState={dState} />) } const Example = ({abState, cState, dState}) => { const [a, setA] = useState(); const aState = { value: a, setValue: setA }; return ( <> <A abState={abState} aState={aState}/> <B abState={abState}/> <C cState={cState} dState={dState}/> </> ) } const A = ({abState, aState}) => { // 解构使用 const { value: ab, setValue: setAb } = abState; const { value: a, setValue: setA } = aState; return <div>{a}</div> } const B = ({abState}) => { const { value: ab, setValue: setAb } = abState; return <div>{ab}</div> } const C = ({cState, dState}) => { return (<D dState={dState} />) } const D = ({dState}) => { const { value: d, setValue: setD } = dState; return (<div>{d}</div>); }
方案2:用React Context避免层层透传
如果多个层级的组件都依赖同一组状态,直接通过Context共享,彻底消除props透传的问题:
修改后代码示例
// 按使用范围拆分Context const AbContext = React.createContext(); const CDContext = React.createContext(); const App = () => { const [ab, setAb] = useState(); const [c, setC] = useState(); const [d, setD] = useState(); return ( <AbContext.Provider value={{ ab, setAb }}> <CDContext.Provider value={{ c, setC, d, setD }}> <Example /> </CDContext.Provider> </AbContext.Provider> ) } const Example = () => { const [a, setA] = useState(); return ( <> <A a={a} setA={setA}/> <B /> <C /> </> ) } const A = ({a, setA}) => { const { ab, setAb } = useContext(AbContext); return <div>{a}</div> } const B = () => { const { ab, setAb } = useContext(AbContext); return <div>{ab}</div> } const C = () => { return (<D />) } const D = () => { const { d, setD } = useContext(CDContext); return (<div>{d}</div>); }
拆分多个Context可以避免单个Context过于庞大,也能让组件只订阅自己需要的状态。
方案3:自定义Hook封装状态逻辑
把关联的状态和操作封装成自定义Hook,组件通过调用Hook直接获取状态,减少props传递:
修改后代码示例
// 封装ab相关状态逻辑 const useAbState = () => { const [ab, setAb] = useState(); // 可在此添加ab相关的业务逻辑 return { ab, setAb }; }; // 封装cd相关状态逻辑 const useCDState = () => { const [c, setC] = useState(); const [d, setD] = useState(); return { c, setC, d, setD }; }; const App = () => { const abState = useAbState(); const cdState = useCDState(); return (<Example {...abState} {...cdState} />) } const Example = () => { const [a, setA] = useState(); const abState = useAbState(); return ( <> <A {...abState} a={a} setA={setA}/> <B {...abState}/> <C {...useCDState()}/> </> ) } // 组件直接调用Hook获取状态 const A = () => { const { ab, setAb } = useAbState(); const [a, setA] = useState(); return <div>{a}</div> } const B = () => { const { ab, setAb } = useAbState(); return <div>{ab}</div> } const C = () => { const { d, setD } = useCDState(); return (<D />) } const D = () => { const { d, setD } = useCDState(); return (<div>{d}</div>); }
注意:如果需要组件间共享同一份状态,这种方式要配合Context或状态管理库,否则不同组件调用Hook会生成独立的状态实例。
方案4:按功能拆分组件
根据状态的使用范围拆分组件,让每个组件只负责自己相关的状态和UI,缩小状态作用域:
修改后代码示例
const App = () => { return ( <> <AbSection /> <CDSection /> </> ) } // 封装ab相关的所有UI和状态 const AbSection = () => { const [ab, setAb] = useState(); const [a, setA] = useState(); return ( <> <A a={a} setA={setA} ab={ab} setAb={setAb}/> <B ab={ab} setAb={setAb}/> </> ) } // 封装cd相关的所有UI和状态 const CDSection = () => { const [c, setC] = useState(); const [d, setD] = useState(); return <C c={c} setC={setC} d={d} setD={setD}/> } // 子组件保持原有逻辑,只接收自身需要的props const A = ({ab, setAb, a, setA}) => { return <div>{a}</div> } const B = ({ab, setAb}) => { return <div>{ab}</div> } const C = ({c, setC, d, setD}) => { return (<D d={d} setD={setD} />) } const D = ({d, setD}) => { return (<div>{d}</div>); }
这种方式让状态的作用域更清晰,彻底避免跨层级的props透传问题。
内容的提问来源于stack exchange,提问作者de3
相关产品推荐
相关产品推荐

