【问题标题】:Make code execute only on the first react-query success使代码仅在第一次反应查询成功时执行
【发布时间】:2023-01-21 02:07:09
【问题描述】:

我有一个奇怪的案例,我需要允许标准的 useQuery 行为,但同时只需要在第一个 onSuccess 上运行一段代码。

我知道我可以通过 useRef 实现这一点,但是有没有办法解决这个问题?

我无法 1:1 重现问题,但您可以在第一个增量计数器重新设置为 100 后看到它。

  const [counter, setCounter] = useState(0);
  const { data, isLoading } = useQuery(
    ["key"],
    () => {
      return axios.get("https://picsum.photos/200");
    },
    {
      onSuccess() { // this runs twice
        setCounter(100);
        console.log("fetch");
      }
    }
  );

  return (
    <div className="App">
      <button
        onClick={() => {
          setCounter(counter + 1);
        }}
      >
        counter {counter}
      </button>
      {data?.data && "data is present"}
    </div>
  );

https://codesandbox.io/s/react-query-once-66lows?file=/src/App.tsx

【问题讨论】:

  • 我想知道这样的用例在实践中会是什么?我们内部有一个 dataUpdateCount,如果我们要在 useQuery 的结果中公开它,您可以检查它是否等于 1。我只是想知道为什么需要这样做?
  • @TkDodo 我可能处理错了,但长话短说,我有一系列从 b/e 返回的项目。然后将数组构建到树中,用户可以对其进行编辑。因此,从查询返回的第一个数据被设置为展开的树元素(在我们的例子中是 1 层深),然后用户可以关闭或进一步打开。如果我不检查,扩展数组将始终更改,因为用户从 b/e 获取数据。我的组件可能有一些重新渲染问题,但在解决这个问题之前(假设这是一个重新渲染问题),我需要一个解决方法。

标签: javascript reactjs typescript react-query


【解决方案1】:

使用 lodash 的once 像这样

  const [counter, setCounter] = useState(0);
  const onSuccess = _.once(() => {
        setCounter(100);
        console.log("fetch"));
      }
  const { data, isLoading } = useQuery(
    ["key"],
    () => {
      return axios.get("https://picsum.photos/200");
    },
    {
      onSuccess
    }
  );

  return (
    <div className="App">
      <button
        onClick={() => {
          setCounter(counter + 1);
        }}
      >
        counter {counter}
      </button>
      {data?.data && "data is present"}
    </div>
  );

【讨论】:

  • 是的,这也有道理
【解决方案2】:

简单的 hack 是添加另一个布尔状态,如 const [isRan, switchIsRan] = useState(false),然后在运行任何逻辑之前检查它在 onSuccess() 中的值,并在该回调结束时将其切换为 true

【讨论】:

  • 是的好点。可能会坚持一个状态。谢谢
  • 我最终使用了这个解决方案。如果有人提出另一个不使用stateref 的方法,请告诉我;)
【解决方案3】:

几天前我遇到了一个类似的问题,发现这个GH thread 提出了这个解决方案,对我有用:

const [counter, setCounter] = useState(0);

const { data, isLoading } = useQuery(
    ["key"],
    () => axios.get("https://picsum.photos/200").then(res => {
       // This will only run once
       setCounter(100);
       return res;
    })
  );

  return (
    <div className="App">
      <button
        onClick={() => {
          setCounter(counter + 1);
        }}
      >
        counter {counter}
      </button>
      {data?.data && "data is present"}
    </div>
  );

【讨论】:

    【解决方案4】:

    一段时间后,我们找到了更好的解决方案。我们可以简单地将staleTime设置为Infinity

    https://react-query-v3.tanstack.com/reference/useQuery#_top

    这允许我们仍然使数据无效并在需要时重新获取,但在它无效之前,查询不会重新获取

    https://react-query-v3.tanstack.com/guides/query-invalidation#query-matching-with-invalidatequeries

    我们可能还需要禁用重新获取窗口焦点

    【讨论】:

      【解决方案5】:

      我有类似的需求(查询由用户在 WYSIWYG 编辑器中更新)并且只需要获取一次初始数据。

      我没有使用 onSuccess 回调,而是做了类似的事情:

      const [initialData, setInitialData] = useState(false);
      const { data, isLoading } = useQuery(...);
      // sets initialData if not set yet and data has been fetched
      if (!initialData && !isLoading && data) {
        setInitialData(data);
      }
      

      【讨论】:

        猜你喜欢
        • 2022-11-23
        • 1970-01-01
        • 2021-11-06
        • 2020-06-16
        • 1970-01-01
        • 1970-01-01
        • 2021-01-18
        • 2018-05-06
        • 1970-01-01
        相关资源
        最近更新 更多