【问题标题】:How to dispatch action in Custom Hooks by useReducer and useContext?如何通过 useReducer 和 useContext 在 Custom Hooks 中调度操作?
【发布时间】:2020-10-18 13:30:41
【问题描述】:

我为按钮切换创建了一个示例。

这是由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>
  );
}

不确定那里发生了什么。但我认为调度状态有问题。

(我试过如果有效载荷没有处理状态,就像一些硬代码一样,所以此时应该触发调度)

有人可以帮帮我吗?欣赏!!!

【问题讨论】:

    标签: javascript reactjs react-hooks use-reducer use-context


    【解决方案1】:

    您是正确的,切换需要是一个函数,但是您正在调度操作类型 UPDATE 并且减速器对该操作没有执行任何操作。

    Dennis 是正确的,您提供上下文的初始值没有意义,也可以将其留空,因为提供者将提供该值。

    Dennis 的 useMemo 建议不会优化您的示例,因为当状态更改时 App 会重新渲染,因此永远不会使用 memoized 值。

    这是我更改的 cmets 代码的工作示例:

    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>

    【讨论】:

      【解决方案2】:

      嗯,没有这样的事情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 实际上是应该如何使用上下文,尽管通常,您会想要添加一个额外的记忆步骤(取决于用例),因为在每次渲染时您都会分配一个新对象 @ 987654328@ 总是会导致渲染,即使state 可能相同。

      查看这样的记忆用例示例。

      如果我的观点不明确,请参阅HMR 评论:

      如果有很多组件访问 上下文然后记忆是一个好主意,当组件具有 提供程序出于更改上下文以外的原因重新呈现。

      【讨论】:

      • useMemo 是没有意义的,如果 App 是根组件,那么它只会在上下文值更改时重新渲染,因此永远不会使用记忆值。如果 App 有其他原因重新渲染,那么它会优化事物,但由于 App 通常是根组件,它可能不会因为上下文值更改以外的原因重新渲染。
      • 这不是毫无意义的,正如我提到的“取决于用例”,这是我的观点的一个例子:codesandbox.io/s/black-smoke-6lz6k?file=/index.js
      • 在您的示例中,上下文永远不会真正改变,您最好使用&lt;AppContext.Provider value={setState}&gt; 而不是 useMemo。我同意在某些情况下 useMemo 很有用,但正如我之前所说的那样; OP 的组件仅在上下文更改时才会重新呈现,从而使 useMemo 永远不会记住任何内容。
      • 这不是解决 OP 问题的评论,而是建议,“虽然通常”,“取决于用例”
      • 是的,如果许多组件访问上下文并且具有提供程序的组件由于更改上下文以外的原因重新呈现,则应该使用策略 useMemo 是正确的。
      猜你喜欢
      • 2020-01-27
      • 1970-01-01
      • 2020-11-18
      • 2021-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-29
      相关资源
      最近更新 更多