Context Module Functions允许您将一组复杂的状态更改封装到一个实用函数中,该实用函数可以进行树形抖动和延迟加载。(tree-shaken and lazily loaded.)
一个简单Context和 reducer 组合的示例:
import React from "react";
const CounterContext = React.createContext()
const actionTypes = { //防止写错,
increment: 'increment',
decrement: 'decrement',
}
function CounterProvider({step=1,initialCount=0,...props}){
const [state,dispatch] = React.useReducer(
(state,action) =>{
const change = action.step??step
switch (action.type) {
case actionTypes.increment:
return {...state,count:state.count+change}
case actionTypes.decrement:
return {...state,count:state.count-change}
default:
throw new Error(`没有这个${action.type}`)
}
},
{count:initialCount}
)
const value =[state,dispatch]
return <CounterContext.Provider value={value} {...props} />
}
function useCounter(){
const context = React.useContext(CounterContext)
if(context===undefined){
throw new Error(`useCounter must be used within a CounterProvider`)
}
return context
}
export {CounterProvider,useCounter}
// src/screens/counter.js
import {useCounter} from 'context/counter'
function Counter() {
const [state, dispatch] = useCounter()
const increment = () => dispatch({type: 'increment'})
const decrement = () => dispatch({type: 'decrement'})
return (
<div>
<div>Current Count: {state.count}</div>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
)
}
// src/index.js
import {CounterProvider} from 'context/counter'
function App() {
return (
<CounterProvider>
<Counter />
</CounterProvider>
)
}
import React from "react";
const CounterContext = React.createContext()
const actionTypes = { //防止写错,
increment: 'increment',
decrement: 'decrement',
}
function CounterProvider({step=1,initialCount=0,...props}){
const [state,dispatch] = React.useReducer(
(state,action) =>{
const change = action.step??step
switch (action.type) {
case actionTypes.increment:
return {...state,count:state.count+change}
case actionTypes.decrement:
return {...state,count:state.count-change}
default:
throw new Error(`没有这个${action.type}`)
}
},
{count:initialCount}
)
const value =[state,dispatch]
return <CounterContext.Provider value={value} {...props} />
}
function useCounter(){
const context = React.useContext(CounterContext)
if(context===undefined){
throw new Error(`useCounter must be used within a CounterProvider`)
}
return context
}
export {CounterProvider,useCounter}
// src/screens/counter.js
import {useCounter} from 'context/counter'
function Counter() {
const [state, dispatch] = useCounter()
const increment = () => dispatch({type: 'increment'})
const decrement = () => dispatch({type: 'decrement'})
return (
<div>
<div>Current Count: {state.count}</div>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
)
}
// src/index.js
import {CounterProvider} from 'context/counter'
function App() {
return (
<CounterProvider>
<Counter />
</CounterProvider>
)
}
我想专注于我们的 reducer(
Counter
组件)的用户。他们必须创建自己的increment
和decrement
调用dispatch
. 这不是一个好的的 API。dispatch
当您有一系列需要调用的函数时(就像您将在我们的练习中看到的那样),它变得更加烦人。第一个倾向是创建“辅助”函数并将它们包含在上下文中。让我们这样做。你会注意到我们必须把它放进去,
React.useCallback
这样我们才能在依赖列表中列出我们的“帮助”函数):
const increment = React.useCallback( () => dispatch({type: 'increment'}), [dispatch], ) const decrement = React.useCallback( () => dispatch({type: 'decrement'}), [dispatch], ) const value = {state, increment, decrement} return <CounterContext.Provider value={value} {...props} /> // now users can consume it like this: const {state, increment, decrement} = useCounter()
const increment = React.useCallback( () => dispatch({type: 'increment'}), [dispatch], ) const decrement = React.useCallback( () => dispatch({type: 'decrement'}), [dispatch], ) const value = {state, increment, decrement} return <CounterContext.Provider value={value} {...props} /> // now users can consume it like this: const {state, increment, decrement} = useCounter()
这个方案不是不好的
辅助方法是对象垃圾,我们需要重新创建和比较它们,除了表面上看起来更好看的语法之外没有其他目的。
(Helper methods are object junk that we need to recreate and compare for no purpose other than superficially nicer looking syntax.)
推荐的(以及 Facebook 所做的)是传递调度。为了解决我们最初试图解决的烦恼,他们使用了接受的可导入“助手”
dispatch
。让我们看一下它的外观:
// src/context/counter.js const CounterContext = React.createContext() function CounterProvider({step = 1, initialCount = 0, ...props}) { const [state, dispatch] = React.useReducer( (state, action) => { const change = action.step ?? step switch (action.type) { case 'increment': { return {...state, count: state.count + change} } case 'decrement': { return {...state, count: state.count - change} } default: { throw new Error(`Unhandled action type: ${action.type}`) } } }, {count: initialCount}, ) //也可以直接放在这里 const value = [state, dispatch] return <CounterContext.Provider value={value} {...props} /> } function useCounter() { const context = React.useContext(CounterContext) if (context === undefined) { throw new Error(`useCounter must be used within a CounterProvider`) } return context } const increment = dispatch => dispatch({type: 'increment'}) const decrement = dispatch => dispatch({type: 'decrement'}) export {CounterProvider, useCounter, increment, decrement} // src/screens/counter.js import {useCounter, increment, decrement} from 'context/counter' function Counter() { const [state, dispatch] = useCounter() return ( <div> <div>Current Count: {state.count}</div> <button onClick={() => decrement(dispatch)}>-</button> <button onClick={() => increment(dispatch)}>+</button> </div> ) }
// src/context/counter.js const CounterContext = React.createContext() function CounterProvider({step = 1, initialCount = 0, ...props}) { const [state, dispatch] = React.useReducer( (state, action) => { const change = action.step ?? step switch (action.type) { case 'increment': { return {...state, count: state.count + change} } case 'decrement': { return {...state, count: state.count - change} } default: { throw new Error(`Unhandled action type: ${action.type}`) } } }, {count: initialCount}, ) //也可以直接放在这里 const value = [state, dispatch] return <CounterContext.Provider value={value} {...props} /> } function useCounter() { const context = React.useContext(CounterContext) if (context === undefined) { throw new Error(`useCounter must be used within a CounterProvider`) } return context } const increment = dispatch => dispatch({type: 'increment'}) const decrement = dispatch => dispatch({type: 'decrement'}) export {CounterProvider, useCounter, increment, decrement} // src/screens/counter.js import {useCounter, increment, decrement} from 'context/counter' function Counter() { const [state, dispatch] = useCounter() return ( <div> <div>Current Count: {state.count}</div> <button onClick={() => decrement(dispatch)}>-</button> <button onClick={() => increment(dispatch)}>+</button> </div> ) }
**可能看起来有点矫枉过正,而且确实如此。**但是,在某些情况下,这种模式不仅可以帮助您减少重复,还 可以帮助提高性能 并帮助您避免依赖项列表中的错误。
不会一直推荐这个,但有时它会有所帮助!
📜 如果需要查看上下文 API,以下是文档:
🦉 React DevTools–区分显示
const MyContext = React.createContext() MyContext.displayName = 'MyContext'
const MyContext = React.createContext() MyContext.displayName = 'MyContext'