ReactJS便捷传递数据至子组件的辅助组件需求及示例
这场景太熟悉了——不想一层一层手动传递props,希望子组件能自动获取上层指定的数据对吧?咱们可以利用React的Context API来实现这个<Block>辅助组件,完美解决props drilling的痛点,让数据传递更简洁。
实现思路
核心思路是通过嵌套的Context Provider来维护当前层级的数据上下文:
- 当
<Block>接收data属性时,它会作为当前层级的基础数据 - 当
<Block>接收name属性时,它会从父级上下文中取出对应字段的值,作为当前层级的上下文数据 - 子组件可以通过自定义Hook直接获取当前上下文里对应
name的字段值
1. 创建数据上下文和工具Hook
首先我们需要创建一个React Context,以及一个自定义Hook来方便子组件消费数据:
import { createContext, useContext, useMemo } from 'react'; // 创建数据上下文,初始值为空对象 const DataContext = createContext({}); // 自定义Hook:获取当前上下文的数据,并根据name取出对应值 export const useData = (name) => { const contextData = useContext(DataContext); return name ? contextData[name] : contextData; };
2. 实现<Block>组件
<Block>组件的核心是合并父级上下文和自身的data/name属性,然后提供新的Context:
export const Block = ({ data, name, children }) => { // 获取父级上下文的数据 const parentData = useContext(DataContext); // 计算当前层级要提供的数据 const currentData = useMemo(() => { if (data) { // 如果传入了data,直接用这个作为当前上下文(若需和父级合并,可改为{...parentData, ...data}) return data; } if (name) { // 如果传入了name,从父级上下文取出对应字段作为当前上下文 return parentData[name] || {}; } // 什么都没传则继承父级上下文 return parentData; }, [parentData, data, name]); return ( <DataContext.Provider value={currentData}> {children} </DataContext.Provider> ); };
注:如果需要支持父级data和传入的data合并,可把return data改成return {...parentData, ...data},根据业务需求调整即可。
3. 实现消费数据的子组件(比如TextBox)
现在子组件可以通过useDataHook轻松获取对应的数据,比如示例中的<TextBox>:
export const TextBox = ({ name }) => { const value = useData(name); return <input type="text" value={value || ''} readOnly />; };
完整示例代码
把所有部分整合起来,就是你给出的使用场景的完整实现:
import { createContext, useContext, useMemo } from 'react'; const DataContext = createContext({}); export const useData = (name) => { const contextData = useContext(DataContext); return name ? contextData[name] : contextData; }; export const Block = ({ data, name, children }) => { const parentData = useContext(DataContext); const currentData = useMemo(() => { if (data) { return data; } if (name) { return parentData[name] || {}; } return parentData; }, [parentData, data, name]); return ( <DataContext.Provider value={currentData}> {children} </DataContext.Provider> ); }; export const TextBox = ({ name }) => { const value = useData(name); return <input type="text" value={value || ''} readOnly />; }; // 你的Test组件 const Test = () => { const data = { loginForm: { name: "Batman", password: "hard password" }, email: "test@gmail.com" }; return ( <Block data={data}> <div> <TextBox name="email" /> </div> <form> <Block name="loginForm"> <div> <TextBox name="name" /> </div> <div> <TextBox name="password" /> </div> </Block> </form> </Block> ) };
扩展优化建议
- 添加类型支持:如果用TypeScript,可以给Context、组件、Hook加上泛型,让类型更安全
- 处理空值:在
useData里可以添加默认值参数,比如useData(name, defaultValue),避免渲染空输入框 - 支持数据更新:如果需要子组件修改数据,可以在Context里加入setter函数,实现双向绑定
- 性能优化:可以用
memo包裹<Block>和子组件,避免不必要的重渲染
内容的提问来源于stack exchange,提问作者Bogdan
相关产品推荐
相关产品推荐

