简体   繁体   English

基于道具的初始 state 在 React 中总是不好的吗?

[英]Is initial state based on props always bad in React?

It's a common React knowledge that having state initialized by props is bad if we don't make them in sync. React 的一个常见知识是,如果我们不使它们同步,则由 props 初始化 state 是不好的。 This is considered fine:这被认为很好:

import { useState, useEffect } from 'react';

export default function MyInput({ initialValue }) {
    const [value, setValue] = useState(initialValue);

    useEffect(
        () => setValue(initialValue),
        [initialValue]
    );

    return (
        <>
            <h1>The value is {value}</h1>
            <input
                type="text"
                value={value}
                onChange={event => setValue(event.target.value)}
            />
        </>
    );
}

But what if I actually don't want to update the value when initialValue changes and want to remove the useEffect() here?但是,如果我实际上不想在initialValue更改时更新值并想在此处删除useEffect()怎么办? Is it strongly against React philosophy?它强烈反对 React 哲学吗? It makes sense in my case, as I actually don't want to update this input value when something else changes the value passed as initialValue .这对我来说很有意义,因为我实际上不想在其他东西更改作为initialValue传递的值时更新此输入值。 I don't want users to lose their input when that happens.我不希望用户在发生这种情况时丢失他们的输入。

How bad is it?有多糟糕?

In essence, there's nothing wrong with using a prop as the initial value of a state variable, AFAIK.本质上,使用 prop 作为 state 变量 AFAIK 的初始值并没有错。

However, in your example you're doing something that is kind of nonsensical: You are defining a state variable which is initialized with the value of a prop, and then every time the prop updates you update your state with the same value.但是,在您的示例中,您正在做一些荒谬的事情:您正在定义一个 state 变量,该变量使用道具的值进行初始化,然后每次道具更新时,您都会使用相同的值更新您的 state 。 Regardless of whether it's an anti-pattern or not, it makes no sense - just use the prop directly, you're doing extra work for no profit.不管它是否是反模式,它都没有意义 - 直接使用 prop,你正在做无利可图的额外工作。 If you remove the useEffect you'll get a very valid use for a prop as an initial value of a state variable.如果您删除useEffect ,您将非常有效地使用 prop 作为 state 变量的初始值。

The question of using a derived state in React.js is often misunderstood, which this StackOverflow question proves.在 React.js 中使用派生 state 的问题经常被误解, 这个 StackOverflow 问题证明了这一点。

In the provided code example from the question, it is unclear why a derived state is being used when the initialValue prop could be used directly.在问题提供的代码示例中,不清楚为什么在可以直接使用initialValue道具时使用派生的 state。 For the sake of clarity, using useEffect for this purpose would be considered an antipattern.为了清楚起见,为此目的使用useEffect将被视为反模式。 Instead, you should check for changes yourself, as demonstrated in the React documentation相反,您应该自己检查更改,如React 文档中所示

However, if the EmailInput component does some modification on the initialValue , such logic will unnecessarily pollute the parent component, if we followed the"rule of lifting state up" (Which I believe the author attempts to explain in this comment ).但是,如果EmailInput组件对initialValue做了一些修改,如果我们遵循“提升 state 的规则” (我相信作者试图在此评论中解释),这样的逻辑将不必要地污染父组件。

In this case, I would argue that the antipattern may be an acceptable choice if used sparingly.在这种情况下,我认为如果谨慎使用,反模式可能是一个可以接受的选择。 Robin Wieruch blog post where said antipattern is used.使用上述反模式的Robin Wieruch 博客文章

An alternative solution is to use the key attribute ( useful in this case ), but this is only effective if the key and initialValue are based off different states.另一种解决方案是使用key属性( 在这种情况下很有用),但这仅在 key 和initialValue基于不同状态时才有效。 Otherwise, it may lead to duplicate renderings.否则,可能会导致重复渲染。

Example with the key attribute具有key属性的示例

// EmailInput.jsx
export default function EmailInput({ initialValue, onChange }) {
    const [value, setValue] = useState(initialValue);

    const handleChange = (event) => {
        const newValue = event.target.value;
        // do some modification on the newValue
        setValue(newValue);
        onChange(newValue); // pass value to parent
    };

    return (
        <>
            <h1>The Email is {value}</h1>
            <input type="text" value={value} onChange={handleChange} />
        </>
    );
}
// Checkout.jsx
export function Checkout() {
    const [user, setUser] = useState({
        id: 1,
        email: "example@example.com",
    });
    return (
        <>
            <EmailInput
                initialValue={user.email}
                key={user.id}
                onChange={(value) => setUser({ id: user.id, email: value })}
            />
            <button
                onClick={() => setUser({id: 2, email: "foo@bar.com"})}
            >
                Update user
            </button>
        </>
    );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM