简体   繁体   English

如何通过 useReducer 和 useContext 在 Custom Hooks 中调度操作?

[英]How to dispatch action in Custom Hooks by useReducer and useContext?

I created a sample for a button toggle.我为按钮切换创建了一个示例。

This is done by useContext (store the data) and useReducer (process the data).这是由useContext (存储数据)和useReducer (处理数据)完成的。 and it is working fine.它工作正常。

Here's the CodeSandBox Link to how it works.这是CodeSandBox 链接到它是如何工作的。

version 1 is just dispatch when clicking the button. version 1只是在单击按钮时调度。

Then I created a version 2 of toggling.然后我创建了一个version 2的切换。 basically just put the dispatch inside a custom hook.基本上只是将调度放在自定义钩子中。 but somehow, it doesn't work.但不知何故,它不起作用。

// 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>
  );
}

Not sure what's going there.不知道那里发生了什么。 but I think there's something wrong with the dispatched state.但我认为派出的 state 有问题。

(I tried it works if the payload is not processing state, like some hard code stuff, so the dispatch should be fired at this moment) (如果有效载荷没有处理 state,我试过它可以工作,就像一些硬代码的东西,所以此时应该触发调度)

Could someone give me a hand?有人可以帮我一把吗? Appreciate!!!欣赏!!!

You are correct that toggle needs to be a function but you are dispatching action type UPDATE and the reducer doesn't do anything with that action.您是正确的,切换需要是 function 但您正在调度操作类型 UPDATE 并且减速器不会对该操作执行任何操作。

Dennis is correct that there is no point in the initial value you are giving the context and may as well leave it empty as the provider will provide the value.丹尼斯是正确的,您提供上下文的初始值没有意义,并且最好将其留空,因为提供者将提供该值。

The useMemo suggestion from Dennis will not optimize your example since App re renders when state changes so the memoized value will never be used. Dennis 的 useMemo 建议不会优化您的示例,因为 App 在 state 更改时重新渲染,因此永远不会使用记忆值。

Here is a working example of your code with comments what I changed:这是您的代码的一个工作示例,其中包含我更改的注释:

 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>

Well, there no such thing React.dispatch .好吧,没有React.dispatch这样的东西。 Its value is undefined它的值是undefined

export const AppContext = createContext({
  state: initialState,
  // useless
  // dispatch: undefined
  dispatch: React.dispatch
});

// dispatch function won't trigger anything.
const {state, dispatch} = useContext(AppContext);

version 1 is actually how context should be used, although usually, you will want to add an extra memoization step (depending on the use case), because on every render you assign a new object {state,dispatch} which always will cause a render even though state may be the same.版本 1实际上应该如何使用上下文,尽管通常,您会想要添加一个额外的记忆步骤(取决于用例),因为在每次渲染时您分配一个新的 object {state,dispatch}总是会导致渲染即使state可能相同。

See such memoization use case example.请参阅此类记忆用例示例。

编辑 black-smoke-6lz6k

If my point wasn't clear, see HMR comment:如果我的观点不明确,请参阅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.应该使用策略性 useMemo,如果许多组件访问上下文,那么当具有提供程序的组件由于更改上下文以外的原因重新呈现时,记忆是一个好主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM