4

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

这是通过useContext(存储数据)和useReducer(处理数据)完成的。它工作正常。

这是CodeSandBox 链接到它是如何工作的。

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

不知道那里发生了什么。但我认为派遣状态有问题。

(我试过如果有效载荷没有处理状态,比如一些硬代码的东西,它就可以工作,所以此时应该触发调度)

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

4

2 回答 2

2

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

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

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

这是您的代码的一个工作示例,其中包含我更改的注释:

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>

于 2020-06-28T09:26:48.157 回答
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实际上是应该如何使用上下文,尽管通常,您会想要添加一个额外的记忆步骤(取决于用例),因为在每次渲染时,您都会分配一个新对象{state,dispatch},即使state可能是相同的。

请参阅此类记忆用例示例。

编辑 black-smoke-6lz6k

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

应该使用策略性 useMemo,如果许多组件访问上下文,那么当具有提供程序的组件由于更改上下文以外的原因重新呈现时,记忆是一个好主意。

于 2020-06-28T09:00:21.023 回答