【发布时间】:2020-04-30 03:42:02
【问题描述】:
我面临这个问题。 当我有一个 Axios 调用时,它承诺调度一个动作来更新 redux 并执行回调。 但是当执行回调时,redux 状态似乎是陈旧的。
i got a sandbox code for demo here
如果点击getNewDate按钮,控制台会显示redux状态的不同。 当 redux 导致重新渲染时,状态将是正确的。
如何在回调期间获得正确的 redux 状态?
【问题讨论】:
我面临这个问题。 当我有一个 Axios 调用时,它承诺调度一个动作来更新 redux 并执行回调。 但是当执行回调时,redux 状态似乎是陈旧的。
i got a sandbox code for demo here
如果点击getNewDate按钮,控制台会显示redux状态的不同。 当 redux 导致重新渲染时,状态将是正确的。
如何在回调期间获得正确的 redux 状态?
【问题讨论】:
响应总是过时的,这就是 React 钩子的工作原理。它们在创建时对每个单独渲染中的所有变量应用闭包。如果您绝对需要该值在回调函数(或效果)中不会过时,请为其设置一个 ref。
const { response, getNewDate } = useResponse();
const responseRef = useRef(response);
useLayoutEffect(() => {
responseRef.current = response;
}, [response]);
const callbackSuccessful = (data: IResponse) => {
console.log("response is not Stale: " + responseRef.current.newDate);
console.log("should be: " + data.newDate);
};
设置 ref 后,您会清楚地看到响应实际上正在发生变化,并且 responseRef.current 显示的值与 data.newDate 相同。
你必须在这里使用LayoutEffect,因为效果运行的顺序对于回调是错误的。由于 useEffect 在组件重新渲染后运行,而 useLayoutEffect 在组件重新渲染时运行。
您可以通过另一种方式看到 useSelector 工作正常并且正在更新,并且您的 MyPages.tsx 看到该更新是 useEffect 以在更改时记录更改。
useEffect(() => {
console.log(response.newDate)
}, [response]);
如果你想在回调中访问最新的 redux 存储状态而完全没有任何时间问题,useStore 很有帮助,而且它根本不会导致重新渲染。
const store = useStore();
const callbackSuccessful = (data: IResponse) => {
console.log("should be: " + data.newDate);
console.log("Redux store: " + store.getState().apiResponse.newDate);
};
https://codesandbox.io/s/react-typescript-demo-pek65?file=/src/pages/MyPages.tsx
【讨论】:
使用 Zachary Haber 的useLayoutEffect 之前的答案是正确答案。
但我可以分享两个低于标准的解决方案,它们都有自己的问题。
解决方案 1
在按钮上使用key 通知 React 它应该更新按钮对象的范围(即重新安装组件),因为它的一些依赖项已经更新。
import React from "react";
import { useResponse } from "../hooks/useResponse";
import { IResponse } from "../utils/apiDef";
const MyPages = () => {
const { response, getNewDate } = useResponse();
const callbackSuccessful = (data: IResponse) => {
console.log("response is: " + response.newDate)
console.log("callback data is: " + data.newDate)
}
const callbackFail = (data: any) => {
}
const handleButton = () => {
getNewDate(callbackSuccessful, callbackFail)
}
const buttonKey = response.newDate
return <div>
hello world {response.newDate}
<br/>
<button key={buttonKey} type="button" onClick={handleButton}>getNewDate</button>
</div>;
};
export default MyPages;
一个小问题:这将显示之前的结果,即response.newDate 将包含更改key 时的值。
解决方案 2
使用全局变量,例如state
import React from "react";
import { useResponse } from "../hooks/useResponse";
import { IResponse } from "../utils/apiDef";
const state = {
response: undefined
}
const MyPages = () => {
const { response, getNewDate } = useResponse();
state.response = response;
const callbackSuccessful = (data: IResponse) => {
console.log("state response is: " + state.response.newDate)
console.log("callback data is: " + data.newDate)
}
const callbackFail = (data: any) => {
}
const handleButton = () => {
getNewDate(callbackSuccessful, callbackFail)
}
return <div>
hello world {response.newDate}
<br/>
<button type="button" onClick={handleButton}>getNewDate</button>
</div>;
};
export default MyPages;
在回调中,state.response.newDate 等于 data.newDate。
一个小问题:它不再是一个可重用的组件。只有在您的应用程序中有一个 MyPages 对象时,此解决方案才有效。所有实例都将指向同一个 static 全局变量,这将引入最后写入者获胜的竞争。
你不应该这样做:
<MyPages />
<MyPages />
这个解决方案的另一个问题:React 的优化;我不知道这会如何影响它。
我希望这会有所帮助。
【讨论】: