前言
userReducer
可以帮助我们集中式的处理复杂的state
管理。但如果我们的页面是多层组件组成,我们如何在子组件中触发state
变化呢?此时useContext
就上场了。
例子
此时我们需要做如下几件事情:
- 创建
globalState.jsx
文件用来存放全局state
,并初始化provider
- 在
app.jsx
文件,也就是入口文件处引入provider
- 在子组件中使用
useContext
globalState.jsx文件
// globalState.jsx
// 创建默认状态值,store
const initState = {
title: '',
isShow: false,
}
// 定义action,作为dispatch的参数
const actionType = {
SET_TITLE: 'SET_TITLE',
SET_SHOW: 'SET_SHOW',
}
// 处理dispatch推送过来的数据
function reducer (state, action) {
switch (action.type) {
case actionType.SET_TITLE:
return { ...state, title: action.payload }
case actionType.SET_SHOW:
return { ...state, isShow: true }
default:
reutrn state
}
}
// 创建context全局上下文
const GlobalContext = createContext({}) // 注:使用ts写时createContext需要传1到2个参数,可用{}代替,否则会报错
或
const GlobalContext = createContext({ state: initState, dispatch(action) => {} })
// 定义provider
function GlobalProvider(props) {
// 使用useReducer来初始化全局state
const [state, dispatch] = useReducer(reducer, initState)
return (
<GlobalContext.Provider value={{ state, dispatch }}>
{props.children} // 接收传入的值
</GlobalContext.Provider>
)
}
// 将context相关的要用到的内容导出
export {
GlobalProvider,
GlobalContext,
initState,
actionType,
reducer,
}
在ts
中createContext
的写法:
参考文档: stackoverflow.com/questions/5…
interface IContextProps {
state: IState;
dispatch: ({type}:{type:string}) => void;
}
const AdminStore = React.createContext({} as IContextProps);
或
// ts写法
interface StoreAction {
type: string
payload?: any
}
const GlobalContext = createContext({
state: defaultData,
dispatch: (action: StoreAction) => {},
})
// 并关闭 .eslintrc 规则
rules: {
'@typescript-eslint/no-empty-function': 'off',
},
app.jsx入口文件
// 引入Store
import { GlobalProvider } from 'xxxxxx'
export function Root() {
return (
<GlobalProvider>
// 子组件
<ChildComponent />
</GlobalProvider>
)
}
在ChildComponent中使用useContext
// ChildComponent.tsx
import { actionType, GlobalContext } from 'xxxxxx'
function ChildComponent() {
const { state, dispatch } = useContext(GlobalContext)
useEffect(() => {
dispatch({ type: actionType.SET_SHOW })
},[])
return (
{ state.isShow ? <div>{state.name}<div> : <Fragment /> }
)
}
从上述例子中可以看出用useReducer+useContext
,可以通过context
把dispatch
函数提供给组件树中的所有组件使用,而不用通过props
的方式一层层传递。
使用Context
比回调函数的优势:
- 对比回调函数的自定义命名,
Context
的API
更加明确,可以清晰的知道哪些组件使用了dispatch
、应用中的数据流动和变化。这也是React
单向数据流的优势。 - 更好的性能。如果使用回调函数作为参数传递的话,因为每次
render
函数都会变化,也会导致子组件的rerender
。当然,我们也可以使用useCallback
来解决这个问题,但相比useCallback
,React
官方更推荐使用useReducer
,因为React
会保证dispatch
始终是不变的,不会引起consumer
组件的rerender
。
总结:
- 如果页面的
state
很简单,建议直接使用useState
。 - 如果页面
state
比较复杂(state
是一个对象或者state
非常多散落在各处),建议使用useReducer
。 - 如果页面的组件层级比较深,且子组件需要出发
state
的变化,可以考虑useReducer+useContext
相结合的方式来使用。