遵循DRY原则生成React Context:替代eval的更优写法
问题
现有两段结构高度相似的React Context实现代码:
const CourseContext = createContext() const CourseProvider = ({children}) => { const [course, setCourse] = useState(null); return ( <CourseContext.Provider value={{course, setCourse}}> {children} </CourseContext.Provider> ) }
以及:
const SaleContext = createContext() const SaleProvider = ({children}) => { const [sale, setSale] = useState({name: "A great course", price: 25}); return ( <SaleContext.Provider value={{sale, setSale}}> {children} </SaleContext.Provider> ) }
为了避免重复编写这类结构,我希望能根据状态变量名(比如"course"或"sale")和初始值(比如null或{name: "A great course", price: 25})快速生成Context。我知道用eval可以实现,但有没有更简洁、更符合JavaScript惯用写法的方法?
解决方案
当然有,完全不用eval,我们可以写一个通用的工厂函数来封装重复逻辑,这是更符合JS最佳实践的方式。
实现通用工厂函数
这个函数接收状态变量名和初始值两个参数,返回包含Context和对应Provider的对象:
import { createContext, useState } from 'react'; function createStateContext(stateName, initialValue) { const Context = createContext(); const Provider = ({ children }) => { const [state, setState] = useState(initialValue); // 动态构造包含状态和修改函数的value对象,比如{course: ..., setCourse: ...} const value = { [stateName]: state, // 自动生成首字母大写的setter函数名 [`set${stateName.charAt(0).toUpperCase()}${stateName.slice(1)}`]: setState }; return ( <Context.Provider value={value}> {children} </Context.Provider> ); }; return { Context, Provider }; }
使用方式
现在生成Course和Sale对应的Context就变得非常简洁:
// 生成Course相关的Context和Provider const { Context: CourseContext, Provider: CourseProvider } = createStateContext('course', null); // 生成Sale相关的Context和Provider const { Context: SaleContext, Provider: SaleProvider } = createStateContext('sale', {name: "A great course", price: 25});
为什么这比eval更好
- 安全性更高:
eval会执行任意字符串,存在代码注入风险,而工厂函数是纯逻辑封装,没有安全隐患 - 可读性更强:代码逻辑清晰,一眼就能看出是生成状态Context的工具函数
- 可维护性更好:后续要修改Provider逻辑(比如添加
useEffect、状态校验),只需要修改工厂函数一处即可
扩展优化(可选)
如果需要自定义修改函数名,或者添加其他共享逻辑,还可以给工厂函数加额外参数:
function createStateContext(stateName, initialValue, setStateName) { // 默认使用首字母大写的setter命名,支持自定义 const finalSetStateName = setStateName || `set${stateName.charAt(0).toUpperCase()}${stateName.slice(1)}`; const Context = createContext(); const Provider = ({ children }) => { const [state, setState] = useState(initialValue); const value = { [stateName]: state, [finalSetStateName]: setState }; return ( <Context.Provider value={value}> {children} </Context.Provider> ); }; return { Context, Provider }; }
自定义修改函数名的使用示例:
const { Context: SaleContext, Provider: SaleProvider } = createStateContext('sale', {name: "A great course", price: 25}, 'updateSale');
内容的提问来源于stack exchange,提问作者AlwaysLearning
相关产品推荐
相关产品推荐

