Redux useSelector未更新,需要刷新

时间:2020-04-30 03:42:02

标签: reactjs redux axios

我面临这个问题。 当我有一个Axios调用时,它承诺会调度一个操作来更新Redux并执行回调。 但是当执行回调时,redux状态似乎已过时。

i got a sandbox code for demo here

如果单击getNewDate按钮,控制台将显示redux状态的差异。 当redux导致重新渲染时,状态将是正确的。

如何在回调过程中获得正确的redux状态?

enter image description here

2 个答案:

答案 0 :(得分:2)

响应始终是陈旧的,这就是React挂钩的工作方式。创建它们时,它们对每个单独渲染中的所有变量应用闭包。如果您绝对需要该值在回调函数(或效果)中不会过时,请为此设置引用。

    environment.production ? // "forFeature" because this is a module of a feature 
                            of my app which is loaded lazily
  [] : HttpClientInMemoryWebApiModule.forFeature(
    AppointmentFakeBackendService, {
        apiBase: 'api'/
    }
  ),

设置引用后,您会清楚地看到响应实际上是在变化的,并且responseRef.current与data.newDate显示相同的值。

您必须在这里使用LayoutEffect,因为对于回调而言,效果运行的顺序是错误的。由于useEffect在组件重新渲染后运行,而useLayoutEffect在组件重新渲染时运行。

另一种方式可以看到useSelector可以正常工作并进行更新,并且MyPages.tsx可以看到更新为useEffect,以便在更改时记录更改。

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

如果您想在回调中访问最新的redux存储状态而根本没有任何时序问题,则useStore会有所帮助,并且完全不会导致重新呈现。

useEffect(() => {
  console.log(response.newDate)
}, [response]);

https://codesandbox.io/s/react-typescript-demo-pek65?file=/src/pages/MyPages.tsx

答案 1 :(得分:0)

使用扎卡里·哈伯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的优化;我不知道这会如何影响它。

我希望这会有所帮助。