【问题标题】:Set a state from useState without triggering useEffect hook从 useState 设置状态而不触发 useEffect 挂钩
【发布时间】:2021-03-01 08:08:47
【问题描述】:

问题

我想防止触发一个 useEffect 挂钩,该挂钩在依赖数组中包含一个 useState 值。为了清楚起见,请看示例。

详解

在初始化过程中,表单会设置一些值,因此我定义了一个标志以防止在完全初始化之前执行其他效果,但我也不想在初始化标志状态更改时触发第二个效果.

我能否以某种方式阻止对 isInitialization 状态的跟踪? 我找不到可以解决我的问题的线程。您是否愿意链接与此相关的线程?提前致谢。

示例

export function SomeComponent(props) {
  const [form] = useForm();
  const [settings, setSettings] = useState(props.initialSettings);
  const [isInitialization, setIsInitialization] = useState(true);

  /**
   * Updates the form settings state, just for initialization
   */
  useEffect(() => {
    if (isInitialization) {
      form.setFieldsValue({
        ...settings,
      });
      setIsInitialization(false); // <-- This should not trigger any useEffect
    }
  }, [settings, form, isInitialization]);

  useEffect(() => {
    if (props.settingsChanges && !isInitialization) {
      props.settingsChanges(settings, isValid && hasRequiredFields);
    }
  }, [settings, props, isInitialization]);
}

【问题讨论】:

  • 移除所有效果依赖,这样效果回调只在组件挂载时运行一次。 React 官方文档是一个很棒的资源:@​​987654321@.
  • 但是初始化过程需要settings, form。我该怎么做呢?
  • 您可以在效果中使用变量,而无需将它们添加到依赖数组中。添加它们会使效果在值更改时运行。
  • 依赖仅用于重新触发效果,它们不用作函数的参数或类似的东西,效果保证至少运行一次。
  • 您是否有机会增强您的 useForm 挂钩以接受 initialFieldValues?因为你的这种方法在我看来是错误的解决方法。

标签: javascript reactjs


【解决方案1】:

尝试从依赖数组中删除isInitialization

  useEffect(() => {
    if (isInitialization) {
      form.setFieldsValue({
        ...settings,
      });
      setIsInitialization(false); // <-- This should not trigger any useEffect
    }
  }, [settings, form]);

As React docs says:

如果你想运行一个效果并且只清理它一次(在挂载和 unmount),您可以传递一个空数组 ([]) 作为第二个参数。这 告诉 React 你的效果不依赖于 props 的任何值 或状态,因此它永远不需要重新运行。这不作为特殊处理 case — 它直接遵循依赖项数组的方式 有效。

更新:

Building Your Own Hooks

构建自己的 Hooks 让您可以将组件逻辑提取到可重用 功能。

如果是通用逻辑,则可以创建自定义钩子并跨组件共享。

«useMyCustomHook.jsx» 文件:

import { useEffect } from 'react';

export default function useMyCustomHook(str) {
  useEffect(() => {
      console.log(`title`, title);
  });
}

然后在组件中使用:

function fooFunction(props) {
  const [firstName, setFirstName] = useState('');

  useMyCustomHook(`This is ${firstName}`);

  return (        
      <div>The firstname is {firstName}</div>
  );
}

【讨论】:

  • 我刚刚把这个github.com/facebook/create-react-app/issues/6880 发红了,这似乎是一个持久的讨论。当我总是需要将这个忽略行粘贴到许多文件中时,我不知道是什么感觉。
  • @LingVu 请看我更新的答案。您可以创建自定义挂钩。然后跨组件共享。
  • 我自己的解决方案并没有解决实际问题,而是用另一种方式解决,但我想避免污染这个问题。无论如何感谢您的贡献。
  • @LingVu 随时发布您的解决方案。看到它真的很有趣。感谢您的反馈:)
  • @DrewReese 你的话对我来说很有价值!谢谢!祝你有美好的一天!:)
【解决方案2】:

特别感谢@Martin。这个解决方案实际上并没有解决跳过 useEffect 触发的问题,而是帮助我进一步重构了我的代码。也许这不相关,但我还是想和你分享我的结果:

最后它看起来像这样。不再需要初始化标志,因为自定义钩子已经这样做了:

const useAntForm = (initalData) => {
  const [form] = useForm();
  form.setFieldsValue({
    ...initalData,
  });

  return form;
};

export function SomeComponent({
  settingsChanges,
  initialSettings,
}: SettingsProps) {
  const form = useAntForm(initialSettings);
  const [settings, setSettings] = useState(initialSettings);
  const [isValid, setIsValid] = useState(false);
  .
  .
  .
}

【讨论】:

  • 请注意,在当前的实现中,form.setFieldsValue(...) 将在每次渲染组件时被调用。
【解决方案3】:

根据https://reactjs.org/docs/hooks-rules.html,当useEffect 钩子不应该触发重新渲染时,可以省略依赖项。通常在省略依赖项时会说

React Hook useEffect 缺少一个依赖项:'someDependency'。要么包含它,要么移除依赖数组。

为了解决这个问题,您只需将 https://www.npmjs.com/package/eslint-plugin-react-hooks 添加到您的 eslint 配置中,该配置主要位于 package.json 文件中

我的配置现在看起来像:

package.json

{
 ...
 "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "plugins": [
      "react-hooks"  // <-- Added this
    ]
  },
  ...
}

【讨论】:

    猜你喜欢
    • 2021-05-24
    • 2021-02-11
    • 2022-01-21
    • 2021-03-16
    • 2021-12-01
    • 2020-05-19
    • 1970-01-01
    • 2020-02-22
    • 2022-01-26
    相关资源
    最近更新 更多