如何让React组件识别调用它的上下文(Context)?
我有一个通用组件Common,会被两个不同的Context(Context1和Context2)的Provider调用。当Context1.Provider包裹它时,组件内部需要执行const context = useContext(Context1);当Context2.Provider包裹时,要执行const context = useContext(Context2)。但这个通用组件怎么识别当前是哪个Context在提供数据,从而执行对应的操作?
实际场景更复杂,比如Common被多层包裹,且两个调用它的组件结构差异很大。以下是简化示例:
通用组件(Common)
export default function Common(){ const context=useContext(/*one of the context*/); context.setData(newData); return ( <div>{context.data}</div> ) }
组件1(Component1)
export const Context1 = createContext({ data, setData }); export default function Component1() { return ( <Context1.Provider value={{data, setData }}> <Common /> </Context1.Provider> ) }
组件2(Component2)
export const Context2 = createContext({ data, setData }); export default function Component2() { return ( <Context2.Provider value={{data, setData }}> <Common /> </Context2.Provider> ) }
解决方案
方案1:通过props传递Context(最直观可控)
直接给Common组件传入需要使用的Context作为props,组件内部用这个props调用useContext,逻辑清晰且易维护。
修改后的Common组件:
export default function Common({ context }) { const ctx = useContext(context); ctx.setData(newData); return ( <div>{ctx.data}</div> ) }
调用时在Provider中传入对应Context:
Component1:
export const Context1 = createContext({ data, setData }); export default function Component1() { return ( <Context1.Provider value={{data, setData }}> <Common context={Context1} /> </Context1.Provider> ) }
Component2同理:
export const Context2 = createContext({ data, setData }); export default function Component2() { return ( <Context2.Provider value={{data, setData }}> <Common context={Context2} /> </Context2.Provider> ) }
方案2:用高阶组件(HOC)封装Context注入
创建一个高阶组件,负责将指定Context注入到Common中,让Common不需要关心具体使用哪个Context,只需要接收注入好的context值。
首先创建HOC:
function withContext(Context) { return function(WrappedComponent) { return function(props) { const context = useContext(Context); return <WrappedComponent {...props} context={context} />; }; }; }
给Common创建两个包装后的组件:
// 绑定Context1的组件 export const CommonWithContext1 = withContext(Context1)(Common); // 绑定Context2的组件 export const CommonWithContext2 = withContext(Context2)(Common);
调用时直接使用对应包装组件:
Component1:
export default function Component1() { return ( <Context1.Provider value={{data, setData }}> <CommonWithContext1 /> </Context1.Provider> ) }
Component2同理:
export default function Component2() { return ( <Context2.Provider value={{data, setData }}> <CommonWithContext2 /> </Context2.Provider> ) }
方案3:通过Context值特征判断(不推荐)
如果两个Context的value有唯一标识(比如自定义的type字段),可以在Common中同时获取两个Context,再判断哪个是当前生效的。但这种方式耦合性高,后续Context结构变动时需要同步修改判断逻辑,维护成本高,仅适合简单场景。
示例:
export default function Common() { const context1 = useContext(Context1); const context2 = useContext(Context2); // 假设Context1的value包含type: 'context1'作为标识 const ctx = context1.type === 'context1' ? context1 : context2; ctx.setData(newData); return ( <div>{ctx.data}</div> ) }
内容的提问来源于stack exchange,提问作者SNORLAX

