如何通过 useReducer 和 useContext 在 Custom Hooks 中调度 action?
How to dispatch action in Custom Hooks by useReducer and useContext?
我创建了一个按钮切换示例。
这是由 useContext
(存储数据)和 useReducer
(处理数据)完成的。它工作正常。
这里是CodeSandBox Link它是如何工作的。
version 1
只是在点击按钮时调度。
然后我创建了一个 version 2
的切换。基本上只是将调度放在自定义挂钩中。但不知何故,它不起作用。
// context
export const initialState = { status: false }
export const AppContext = createContext({
state: initialState,
dispatch: React.dispatch
})
// reducer
const reducer = (state, action) => {
switch (action.type) {
case 'TOGGLE':
return {
...state,
status: action.payload
}
default:
return state
}
}
//custom hook
const useDispatch = () => {
const {state, dispatch} = useContext(AppContext)
return {
toggle: dispatch({type: 'UPDATE', payload: !state.status})
// I tried to do toggle: () => dispatch(...) as well
}
}
// component to display and interact
const Panel = () => {
const {state, dispatch} = useContext(AppContext)
// use custom hook
const { toggle } = useDispatch()
const handleChange1 = () => dispatch({type: 'TOGGLE', payload: !state.status})
const handleChange2 = toggle // ERROR!!!
// and I tried handleChange2 = () => toggle, or, handleChange2 = () => toggle(), or handleChange2 = toggle()
return (
<div>
<p>{ state.status ? 'On' : 'Off' }</p>
<button onClick={handleChange1}>change version 1</button>
<button onClick={handleChange2}>change version 2</button>
</div>
)
}
// root
export default function App() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<AppContext.Provider value={{state, dispatch}}>
<div className="App">
<Panel />
</div>
</AppContext.Provider>
);
}
不确定那里发生了什么。但我认为调度状态有问题。
(我试过如果有效载荷没有处理状态,就像一些硬代码一样,它可以工作,所以此时应该触发调度)
有人可以帮帮我吗?欣赏!!!
好吧,没有这样的东西React.dispatch
。它的值为undefined
export const AppContext = createContext({
state: initialState,
// useless
// dispatch: undefined
dispatch: React.dispatch
});
// dispatch function won't trigger anything.
const {state, dispatch} = useContext(AppContext);
版本 1 实际上是应该如何使用上下文,尽管通常情况下,您会想要添加一个额外的记忆步骤(取决于用例),因为在每次渲染时您分配一个新对象 {state,dispatch}
,它总是会导致渲染,即使 state
可能相同。
查看此类记忆用例示例。
如果我的观点不清楚,请参阅HMR评论:
Strategic useMemo should be used, if many components access the
context then memoizing is a good idea when the component with the
provider re-renders for reasons other than changing the context.
你是正确的,toggle 需要是一个函数,但你正在调度操作类型 UPDATE 并且 reducer 不对该操作执行任何操作。
Dennis 是正确的,您提供上下文的初始值毫无意义,最好将其留空,因为提供者将提供该值。
Dennis 的 useMemo 建议不会优化您的示例,因为 App 在状态更改时重新呈现,因此永远不会使用记忆值。
这是您的代码的工作示例,其中包含我更改的注释:
const { createContext, useReducer, useContext } = React;
const initialState = { status: false };
//no point in setting initial context value
const AppContext = createContext();
const reducer = (state, action) => {
switch (action.type) {
case 'TOGGLE':
return {
...state,
status: action.payload,
};
default:
return state;
}
};
const useDispatch = () => {
const { state, dispatch } = useContext(AppContext);
return {
//you were correct here, toggle
// has to be a function
toggle: () =>
dispatch({
//you dispatch UPDATE but reducer
// is not doing anything with that
type: 'TOGGLE',
payload: !state.status,
}),
};
};
const Panel = () => {
const { state, dispatch } = useContext(AppContext);
const { toggle } = useDispatch();
const handleChange1 = () =>
dispatch({ type: 'TOGGLE', payload: !state.status });
const handleChange2 = toggle; // ERROR!!!
return (
<div>
<p>{state.status ? 'On' : 'Off'}</p>
<button onClick={handleChange1}>
change version 1
</button>
<button onClick={handleChange2}>
change version 2
</button>
</div>
);
};
function App() {
const [state, dispatch] = useReducer(
reducer,
initialState
);
return (
<AppContext.Provider value={{ state, dispatch }}>
<div className="App">
<Panel />
</div>
</AppContext.Provider>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我创建了一个按钮切换示例。
这是由 useContext
(存储数据)和 useReducer
(处理数据)完成的。它工作正常。
这里是CodeSandBox Link它是如何工作的。
version 1
只是在点击按钮时调度。
然后我创建了一个 version 2
的切换。基本上只是将调度放在自定义挂钩中。但不知何故,它不起作用。
// context
export const initialState = { status: false }
export const AppContext = createContext({
state: initialState,
dispatch: React.dispatch
})
// reducer
const reducer = (state, action) => {
switch (action.type) {
case 'TOGGLE':
return {
...state,
status: action.payload
}
default:
return state
}
}
//custom hook
const useDispatch = () => {
const {state, dispatch} = useContext(AppContext)
return {
toggle: dispatch({type: 'UPDATE', payload: !state.status})
// I tried to do toggle: () => dispatch(...) as well
}
}
// component to display and interact
const Panel = () => {
const {state, dispatch} = useContext(AppContext)
// use custom hook
const { toggle } = useDispatch()
const handleChange1 = () => dispatch({type: 'TOGGLE', payload: !state.status})
const handleChange2 = toggle // ERROR!!!
// and I tried handleChange2 = () => toggle, or, handleChange2 = () => toggle(), or handleChange2 = toggle()
return (
<div>
<p>{ state.status ? 'On' : 'Off' }</p>
<button onClick={handleChange1}>change version 1</button>
<button onClick={handleChange2}>change version 2</button>
</div>
)
}
// root
export default function App() {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<AppContext.Provider value={{state, dispatch}}>
<div className="App">
<Panel />
</div>
</AppContext.Provider>
);
}
不确定那里发生了什么。但我认为调度状态有问题。
(我试过如果有效载荷没有处理状态,就像一些硬代码一样,它可以工作,所以此时应该触发调度)
有人可以帮帮我吗?欣赏!!!
好吧,没有这样的东西React.dispatch
。它的值为undefined
export const AppContext = createContext({
state: initialState,
// useless
// dispatch: undefined
dispatch: React.dispatch
});
// dispatch function won't trigger anything.
const {state, dispatch} = useContext(AppContext);
版本 1 实际上是应该如何使用上下文,尽管通常情况下,您会想要添加一个额外的记忆步骤(取决于用例),因为在每次渲染时您分配一个新对象 {state,dispatch}
,它总是会导致渲染,即使 state
可能相同。
查看此类记忆用例示例。
如果我的观点不清楚,请参阅HMR评论:
Strategic useMemo should be used, if many components access the context then memoizing is a good idea when the component with the provider re-renders for reasons other than changing the context.
你是正确的,toggle 需要是一个函数,但你正在调度操作类型 UPDATE 并且 reducer 不对该操作执行任何操作。
Dennis 是正确的,您提供上下文的初始值毫无意义,最好将其留空,因为提供者将提供该值。
Dennis 的 useMemo 建议不会优化您的示例,因为 App 在状态更改时重新呈现,因此永远不会使用记忆值。
这是您的代码的工作示例,其中包含我更改的注释:
const { createContext, useReducer, useContext } = React;
const initialState = { status: false };
//no point in setting initial context value
const AppContext = createContext();
const reducer = (state, action) => {
switch (action.type) {
case 'TOGGLE':
return {
...state,
status: action.payload,
};
default:
return state;
}
};
const useDispatch = () => {
const { state, dispatch } = useContext(AppContext);
return {
//you were correct here, toggle
// has to be a function
toggle: () =>
dispatch({
//you dispatch UPDATE but reducer
// is not doing anything with that
type: 'TOGGLE',
payload: !state.status,
}),
};
};
const Panel = () => {
const { state, dispatch } = useContext(AppContext);
const { toggle } = useDispatch();
const handleChange1 = () =>
dispatch({ type: 'TOGGLE', payload: !state.status });
const handleChange2 = toggle; // ERROR!!!
return (
<div>
<p>{state.status ? 'On' : 'Off'}</p>
<button onClick={handleChange1}>
change version 1
</button>
<button onClick={handleChange2}>
change version 2
</button>
</div>
);
};
function App() {
const [state, dispatch] = useReducer(
reducer,
initialState
);
return (
<AppContext.Provider value={{ state, dispatch }}>
<div className="App">
<Panel />
</div>
</AppContext.Provider>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>