在使用 graphql 的 useQuery 获取的数据更新 useEffect 钩子内的状态变量时防止无限渲染

Posted

技术标签:

【中文标题】在使用 graphql 的 useQuery 获取的数据更新 useEffect 钩子内的状态变量时防止无限渲染【英文标题】:Prevent infinite renders when updating state variable inside useEffect hook with data fetched using useQuery of graphql 【发布时间】:2020-10-12 12:25:45 【问题描述】:

Graphql 提供 useQuery 钩子来获取数据。每当组件重新渲染时都会调用它。

//mocking useQuery hook of graphql, which updates the data variable
  const data = useQuery(false);

我正在使用 useEffect 挂钩来控制应该调用“useQuery”的次数。

我想要做的是每当我从 useQuery 收到数据时,我想对数据执行一些操作并将其设置为另一个状态变量“stateOfValue”,它是一个嵌套对象数据。所以这必须在 useEffect 挂钩内完成。

因此,我需要将我的 stateOfValue 和“data”(这是我的 API 数据)变量作为依赖项添加到 useEffect 挂钩。

  const [stateOfValue, setStateOfValue] = useState(
    name: "jack",
    options: []
  );

  const someOperation = (currentState) => 
    return 
        ...currentState,
        options: [1, 2, 3]
      ;
  
  useEffect(() => 
    if (data) 
      let newValue = someOperation(stateOfValue);
      setStateOfValue(newValue);
    
  , [data, stateOfValue]);

基本上,我将在我的 useEffect 中使用的所有变量添加为依赖项,因为这是正确的做法according to Dan Abramov.

现在,根据反应,状态更新必须在没有突变的情况下完成,因为我每次需要更新状态时都会创建一个新对象。但是通过设置一个新的状态变量对象,我的组件被重新渲染,导致无限渲染。

如何以这样一种方式实现它,即我将所有变量传递给我的 useEffect 依赖数组,并让它只执行一次 useEffect。

请注意:如果我不将 stateOfValue 变量添加到依赖项中,它会起作用,但这是在撒谎。

Here is the reproduced link.

【问题讨论】:

不将stateOfValue 添加到依赖项中可能不会撒谎 - 您不会在 useEffect 中使用它,因此它实际上不是依赖项,省略它很好 【参考方案1】:

我想你误会了 你想在依赖数组中的是[data, setStateOfValue]而不是[data, stateOfValue]。因为您在useEffect 中使用setStateOfValue 而不是stateOfValue 正确的是:

const [stateOfValue, setStateOfValue] = useState(
    name: "jack",
    options: []
  );

  const someOperation = useCallback((prevValue) => 
    return 
        ...prevValue,
        options: [1, 2, 3]
      ;
  ,[])
  useEffect(() => 
    if (data) 
      setStateOfValue(prevValue => 
        let newValue = someOperation(prevValue);
        return newValue
      );
    
  , [data, setStateOfValue,someOperation]);

【讨论】:

否,效果依赖于 someOperation 会导致无限重新渲染或过时关闭 它确实解决了这个问题的荒谬示例,但我猜 oncompleted 会更好地将 graphql 数据复制到本地状态。还;如果无法运行,请不要创建 sn-p。【参考方案2】:

如果要在效果中设置状态,可以执行以下操作:

const data = useQuery(query);
const [stateOfValue, setStateOfValue] = useState();
const someOperation = useCallback(
  () =>
    setStateOfValue((current) => ( ...current, data )),
  [data]
);
useEffect(() => someOperation(), [someOperation]);

每次数据更改时,都会重新创建函数 SomeOperation 并导致效果运行。在某些时候数据被加载或者出现错误并且数据没有被重新创建导致 someOperation 不能被再次创建并且效果不能再次运行。

【讨论】:

【参考方案3】:

首先我会问您是否需要stateOfValue 存储为状态。如果没有(例如,它不会被其他任何东西编辑),您可能会使用 useMemo 挂钩

const myComputedValue = useMemo(() => someOperation(data), [data]);

现在myComputedValue 将是someOperation 的结果,但它只会在数据更改时重新运行

如果需要将其存储为状态,您可以使用useQuery 中的onCompleted 选项

const data = useQuery(query, 
  onCompleted: response => 
    let newValue = someOperation();
    setStateOfValue(newValue);
  
)

【讨论】:

以上是关于在使用 graphql 的 useQuery 获取的数据更新 useEffect 钩子内的状态变量时防止无限渲染的主要内容,如果未能解决你的问题,请参考以下文章

无法在异步方法中获取查询,无法从外部导入 graphql 查询

无法使用 React-Query + Graphql-Request 在 useQuery 中传递参数

如何在 GraphQL 中正确链接 useQuery 和 useMutation?

如何使用 Apollo 客户端 useQuery 钩子防止不必要的重新获取?

如何在第一次渲染和点击时都渲染 useQuery? [ReactJS,graphql]

在 useQuery 后反应组件不重新渲染