【问题标题】:Issues with useReducer not synchronously updating the stateuseReducer 不同步更新状态的问题
【发布时间】:2019-08-13 13:13:52
【问题描述】:

根据React docs:

当你有复杂的情况时,useReducer 通常比 useState 更可取 涉及多个子值或下一个状态时的状态逻辑 取决于上一个。

1.有人能解释一下为什么useReducer 没有同步更新状态吗?

const reducer = (state, action) => {
    if( action.type === 'ADD_VALUE') {
        console.log(`STATE IN REDUCER`, [...state, action.path]) // => ["1.1"]
        return [...state, action.path]
    }   
}

const [state, dispatch] = useReducer(reducer, [])

<input type="button" onClick={() => {
    dispatch({ type: 'ADD_VALUE', path: "1.1"})
    console.log(`STATE`, state) // => []
    // here i want to do some stuff based on the lastest updated state (["1.1"] and not [])
    // for example dispatch an action with redux
}}/>

2.我怎样才能根据最新的更新状态(["1.1"] 而不是[])做一些事情(发送redux 操作)?

【问题讨论】:

  • 状态不会同步更新,即使是React也不会改变当前状态,所以state是对之前状态的引用。
  • 因为state updates are asynchronous。 99.9% 肯定使用钩子不会改变这一点。此外,正如@Tholle 所说,您所指的state 无论如何都会过时。
  • @Tholle 以及如何解决这个问题(查看问题的第二部分)
  • @T.J.Crowder 那怎么解决呢?
  • @Youssef - 当状态改变时,你的组件会被重新渲染。所以像往常一样根据状态进行渲染。

标签: javascript reactjs


【解决方案1】:

使用useEffect 正确访问状态。如果你想在某个条件被满足时调用某些东西,你可以添加一些保护措施。

如果你想跨组件访问你的 reducer,你可以使用 Context API 来存储 reducer。看下面的例子。你可以看到 reducer 被注入到父组件的 Context 中,然后两个子组件 a) 分派一个动作 b) 从动作接收更新。

1.跨多个组件使用的上下文缩减器示例

import React from "react";
import ReactDOM from "react-dom";

const Application = React.createContext({
  state: null,
  dispatch: null
});

function ActionComponent() {
  const { dispatch } = React.useContext(Application);
  return (
      <div>
          <div>Action Component</div>
          <button onClick={() => dispatch("lol")}>Do something</button>
      </div>
  );
}

function ListenerComponent() {
  const { state } = React.useContext(Application);
  React.useEffect(
    () => {
      console.log(state);
    },
    [state]
  );
  return <div>Listener Component</div>;
}

function App() {
  const [state, dispatch] = React.useReducer(function(state = [], action) {
    return [...state, action];
  });
  return (
    <Application.Provider value={{ state, dispatch }}>
      <div className="App">
        <ActionComponent />
        <ListenerComponent />
      </div>
    </Application.Provider>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

2。不使用 Application Context 的本地 reducer 示例

const reducer = (state, action) => {
    if( action.type === 'ADD_VALUE') {
        return [...state, action.path]
    }   
}

const [state, dispatch] = useReducer(reducer, [])

React.useEffect(() => {
    console.log(state);
}, [state]);

<input type="button" onClick={() => {
    dispatch({ type: 'ADD_VALUE', path: "1.1"})
}}/>

【讨论】:

  • 感谢@Win 的提示,它将在未来帮助我,但我在循环中有dispatch,所以我认为这个解决方案不适合我的情况,不是吗?认为?
  • @Youssef 没关系,我给你看一个例子,告诉你如何从不同的组件与同一个 reducer 对话
  • Stack Snippets 支持 React,包括 JSX; here's how to do one。也许你可以让你的例子可以运行?
  • @Youssef 它是否在循环中并不重要,因为动作被发送到队列,一旦这个循环完成,监听器将收到更新的状态。
  • 非常感谢@Win,我会深入研究一下,我会给你反馈
猜你喜欢
  • 1970-01-01
  • 2021-01-17
  • 2022-01-05
  • 1970-01-01
  • 2020-06-07
  • 2021-08-17
  • 1970-01-01
  • 1970-01-01
  • 2021-03-21
相关资源
最近更新 更多