React 组件的依赖问题?

问题描述 投票:0回答:1

今天和同事讨论了React依赖关系。他认为组件所依赖的产生副作用的所有变量都应该放入依赖数组中,例如

import React, { useContext, useEffect, useState } from 'react';

export const Component = props => {
    const { someInfo } = props;

    const [info, setInfo] = useState(null);
    const { updateInfo } = useContext(someInfoContext);

    const initSomething = async publicId => {
        const res = await getPublicInfo(publicId);

        updateInfo(res);
        setInfo(res);
    };

    // Here only someInfo.publicId is used
    useEffect(() => {
        initSomething(someInfo.publicId);
    }, [someInfo.publicId]);

    return <div>hellp world</div>;
};

这里,useEffect中的副作用依赖于变量someInfo.publicId,所以我将其放入依赖数组中,以确保副作用每次发生变化时都可以重新执行。然而,我的同事不同意。他认为useEffect中的所有变量都是变量依赖,所以他认为应该这样写

import React, { useCallback, useContext, useEffect, useState } from 'react';

export const Component = props => {
    const { someInfo } = props;

    const [info, setInfo] = useState(null);
    const { updateInfo } = useContext(someInfoContext);

    const initSomething = useCallback(
        async publicId => {
            const res = await getPublicInfo(publicId);

            updateInfo(res);
            setInfo(res);
        },
        [updateInfo]
    );

    // Here someInfo.publicId and initSomething are used
    useEffect(() => {
        initSomething(someInfo.publicId);
    }, [someInfo.publicId, initSomething]);

    return <div>hellp world</div>;
};


我觉得这样写有一个缺点,就是依赖性具有传染性。必须保证函数的引用不变,所以函数必须用useCallback包装,initSomething中的updateInfo也必须用useCallback处理。这就需要一直处理,带来很大的精神负担,而且代码容易出现问题,死循环。不过,如果像我先写的那样,组件渲染时会重新声明initSomething,但可以保证代码执行逻辑没有问题

我想知道第一种和第二种写法哪个是正确的?

javascript reactjs react-hooks
1个回答
0
投票

问题的两个可能答案是:

a) 当然,只要你是团队中唯一的人,你就可以遵循你认为最好的任何方式。这意味着您是唯一的一名开发人员,也是唯一一个将在软件的整个生命周期中维护该软件的人。

b) 如果情况与 a 点不同,那么遵循社区的最佳实践和库开发人员的建议可能会有所帮助。

这里引用 React 团队用来描述依赖数组被开发人员操纵的情况的确切词。他们将开发人员的这种行为称为“Lie to React”。开发商撒谎了! 是否可以抑制依赖linter?

但是,您的观点是有效的。尽管效果中的整个代码被视为响应式逻辑,但有时效果也会具有非响应式逻辑。 React 团队已经开始着手解决这个问题,因为我们可以看到这一行中提出了 API useEffectEvent。

虽然 linter 可以被抑制并且开发人员可以继续代码,但是这样的代码变得缺乏表达力,容易出现逻辑错误并且与未来的库版本不兼容。因此,将依赖项设置为完整的额外效果,或使用解决方法来避免操作依赖项数组是值得尝试的。

回答问题:

请检查创建函数对象 initSomething 的必要性。 它真的应该是一个命名的功能对象吗?它是否被多个调用站点引用?它可能不是真正的命名者。请参阅下面可能的重构代码。

const initSomething = useCallback(
        async publicId => {
            const res = await getPublicInfo(publicId);

            updateInfo(res);
            setInfo(res);
        },
        [updateInfo]
    );

事实上,如果函数 getPublicInfo 不是异步函数,那么函数 initSomething 可能会被重构如下。这里讨论这个案例只是为了展示重构简化了代码。

useEffect(() => {
    const res =  getPublicInfo(someInfo.publicId);
    updateInfo(res);
    setInfo(res);
}, [someInfo.publicId, updateInfo, setInfo]);

由于 getPublicInfo 是一个异步函数,并且需要使用await在这里进行计算,因此下面的代码是最少的。在这里,立即调用的匿名函数已用于包装调用。 但是,updateInfo 和 setInfo 也需要设置为依赖项,尽管它将保持不变。通过添加它,代码变得更具表现力。

  useEffect(() => {
    (async () => {
      const res = await getPublicInfo(someInfo.publicId);
      updateInfo(res);
      setInfo(res);
    })();
  }, [someInfo.publicId, updateInfo, setInfo]);
© www.soinside.com 2019 - 2024. All rights reserved.