React useEffect 依赖正在触发更新

时间:2021-06-03 05:15:14

标签: reactjs react-hooks

考虑以下 React 组件:

interface Props {
    cpi: number;
    onCpiChange: (value?: number) => void;
}

const Assumption: FunctionComponent<Props> = (props: Props) => {
    const [cpiValue, setCpiValue] = useState<number>();

    useEffect(() => {
        if (props.cpi != cpiValue) {
            setCpiValue(props.cpi);
        }
    }, [props.cpi]);

    return (
        <FormattedNumberInput
            value={cpiValue}
            onBlur={() => props.onCpiChange(cpiValue)}
            onValueChange={setCpiValue}
        />
    );
};

export default Assumption;

useEffect 抱怨它缺少 cpiValue - 由于它不在依赖数组中,因此在使用 props.cpi != cpiValue 进行比较时它不会更新。我不希望每次 useEffect 更改时都触发此 cpiValue。我如何让这个 useEffect 仅在 props.cpi 更改但仍然可以访问所需的其他变量(例如与我进行比较的 cpiValue)时做出响应?

此外,在最初学习 React Hooks 时,我了解到有多种方法可以控制 useEffect 何时触发,如下所示:

  1. 如果您不包含依赖项数组,它将在每次渲染时更新。
  2. 如果你提供一个空数组,它应该只在组件第一次加载时更新
  3. 如果您在数组中提供任何成员,则每次更新这些变量之一时,useEffect 都会触发。

其中唯一有意义的是第三个选项。如果您不将它们包含在依赖数组中,则选项一和二对 useEffect 之外的变量没有作用域,但随后将所需变量添加到依赖数组会将 useEffect 的定义从1 或 2 到 3。我在这里遗漏了什么吗?

2 个答案:

答案 0 :(得分:2)

您可以将条件测试移动到状态更新程序中,这应该将其作为依赖项删除。使用功能状态更新,您可以使用先前状态的 cpiValue 并使用三元返回新的 cpi props value 先前状态 cpiValue 值.

useEffect(() => {
  setCpiValue(cpiValue => props.cpi !== cpiValue ? props.cpi : cpiValue);
}, [props.cpi]);

在我看来,我认为没有必要进行条件测试,因为似乎任何时候 props.cpi 更新并触发 useEffect 回调,您目前仅在更新不存在时才将更新排入队列t 已经等于当前状态。为什么不总是在更新时更新本地状态缓存的 props.cpi 版本?

useEffect(() => {
  setCpiValue(props.cpi);
}, [props.cpi]);

如果它们实际上已经相等,那么它们之后仍然相等。如果值相同,React 可以 bail out of state updates

<块引用>

如果您将 State Hook 更新为与当前状态相同的值, React 将在不渲染子项或触发效果的情况下退出。 (React 使用 Object.is 比较算法。)

请注意,React 可能仍需要再次渲染该特定组件 在保释之前。这不应该是一个问题,因为 React 不会 不必要地“深入”到树中。如果你做的很贵 渲染时进行计算,您可以使用 useMemo 对其进行优化。

答案 1 :(得分:0)

useState 每次调用时都会触发重新渲染。并且更改的 props 也会触发组件重新渲染。 也许您可以使用 useState(initialize)

interface Props {
    cpi: number;
    onCpiChange: (value?: number) => void;
}

const Assumption: FunctionComponent<Props> = (props: Props) => {
    const [cpiValue, setCpiValue] = useState<number>(props.cpi);

    // useEffect(() => {
    //     if (props.cpi != cpiValue) {
    //         setCpiValue(props.cpi);
    //     }
    // }, [props.cpi]);

    return (
        <FormattedNumberInput
            value={cpiValue}
            onBlur={() => props.onCpiChange(cpiValue)}
            onValueChange={setCpiValue}
        />
    );
};

export default Assumption;