如何避免在每个渲染器上触发useEffect?

时间:2019-04-17 09:00:09

标签: javascript reactjs react-hooks

我正在创建一个接受对象的自定义挂钩,然后依次调用useEffect。该对象可以包含内联函数。

const stuff = useValidation({
  fields: {
    username: { /* more nested objects */ }
  },
  onSubmit: () => alert('submitted'),
});

挂钩实现如下:

function useValidation(config) {
  const [state, dispatch] = useReducer(validationReducer);
  useEffect(() => {
    validateFields(config.fields, state);
  }, [config.fields, state]);
}

还有更多内容(here's the actual implementation),但这是相关的部分。

当我内联创建对象时,useEffect连续运行。发生这种情况是因为React使用引用相等(不是深度相等)来检查内容是否已更改。

我可以通过将配置文件包装在useMemo中或将其移动到render方法之外来“修复”此问题。两者都对开发人员的体验不利。

我尝试了Kent C. Dodds的use-deep-compare-effect库,但是由于我在配置中允许使用内联箭头功能,因此它“无法工作”。

最好,我希望能够让用户输入一个内联对象,并让我以某种方式处理所有更改。

我该如何解决?

3 个答案:

答案 0 :(得分:1)

在我看来,useEffect被称为每个渲染,因为传递给useValidation的配置是每次动态创建的。我的建议是将其置于您使用useValidation的组件中或useValidation本身中的某个位置,就像这样:

function useValidation(config) {
  const [storedConfig, setStoredConfig] = useState(config);
  const [state, dispatch] = useReducer(validationReducer);

  useEffect(() => {
    validateFields(config.fields, state);
  }, [storedConfig.fields, state]);
}

如果配置是可更改的,则应将其存储在钩子之外,无论哪种方式,都要确保该配置不是每个渲染的新对象。

答案 1 :(得分:0)

根据文档,您不应将对象或数组放在数组上,而应将其中的各个属性放在其中,如下所示:

function useValidation(config) {
  const [state, dispatch] = useReducer(validationReducer);
  useEffect(() => {
    validateFields(config.fields, state);
  }, [config.fields.username, state]);
}

我不知道您的 state 是什么,但应该是一个不可变的值,例如字符串、数字、布尔值等。

Date 对象相同,它们应该首先转换为 String

答案 2 :(得分:-1)

看起来只是代码中的唯一语法错误。

您的函数应如下图所示

function useValidation(config) {
 const [state, dispatch] = useReducer(validationReducer);
  useEffect(() => {
    validateFields(config.fields, state);
  }, [config.fields, state])
}