来自 react useState 的状态在使用键 属性 时更新,但需要 useEffect 或类似方法才能更新

State from react useState is updated when key property is used but requires useEffect or similar method to update otherwise

我有以下组件:

import React, { useState } from "react";
import { FormControl, TextField } from "@material-ui/core";

interface IProps {
    text?: string;
    id: number;
    onValueChange: (text: string, id: number) => void;
    placeholder: string;
}

export const QuestionTextRow: React.FC<IProps> = (props) => {
    const [item, onItemChange] = useState(props.text);

    const onChange = (e: React.FormEvent<HTMLInputElement>) => {
        const newValue = e.currentTarget.value;

        onItemChange(newValue);
        props.onValueChange(newValue, props.id);
    };

    return (
        <>
            <FormControl fullWidth>
                <div>{props.text}</div>
                <TextField
                    aria-label="question-text-row"
                    onDragStart={(e) => {
                        e.preventDefault();
                        e.stopPropagation();
                    }}
                    value={item}
                    onChange={(ev: React.ChangeEvent<HTMLInputElement>): void => {
                        onChange(ev);
                    }}
                />
            </FormControl>
        </>
    );
};

它是通过以下组件呈现的:

const renderQuestionOptions = (id: number): JSX.Element => {
    const item = props.bases.find((x) => x.sortableId === id);

    if (!item) return <> </>;

    return (
        <div className={classes.questionPremiseRow}>
            <div className={classes.rowOutline}>
                <QuestionOptionsSortableRow item={item} isDisabled={false} onClickRow={onClickBasisRow}>
                    <QuestionTextRow
                        text={item.text ? item.text.text : ""}
                        id={item.sortableId}
                        onValueChange={basisValueChanged}
                        placeholder={intl.formatMessage({ id: "question.create.basis.row.placeholder" })}
                    ></QuestionTextRow>
                </QuestionOptionsSortableRow>
            </div>
        </div>
    );
};

它呈现以下列表:

如您所见,props.text 中的 props.textuseState item 呈现相同。如果 props.text 更新,它不会反映在 useState 上。

我可以通过 useEffect 解决它以使其工作:

useEffect(() => {
    onItemChange(props.text);
}, [props.text]);

https://reactjs.org/docs/hooks-effect.html

但是,如果我将 key={`${item.text?.text}-${item.sortableId}`} 添加到 QuestionTextRow,它会在不使用 useEffect 的情况下工作。怎么会?

我知道应该使用静态唯一键,但如果使用 key={item.uniqueId} 会不会得到相同的结果?

https://www.npmjs.com/package/react-key-index

The argument passed to useState is the initial state much like setting state in constructor for a class component and isn't used to update the state on re-render

However If I add key={${item.text?.text}-${item.sortableId}} to QuestionTextRow it will work without using useEffect. How come?

那是因为reconciliation。在反应中,当你在一个渲染上说:

<SomeComponent key={1}/>

如果在下一次渲染中使用不同的键渲染同一个组件(在同一个地方),比如:

<SomeComponent key={2}/>

React 将销毁与前一个组件相关的实例并为此组件创建一个新实例,因此该组件内的 useState 将再次使用提供的 text 属性 进行初始化(就像您第一次创建组件时一样)。

如果上一次和下一次渲染中的某个组件的密钥相同,而您只是更改了一些其他道具,在这种情况下,组件将被重新渲染(没有实例被破坏),这就是为什么您没有看到 text 属性反映在状态中。


有时像 useEffect 解决方案中那样将 props 复制到 state 可能会很棘手,我建议您阅读这篇 post,它是关于 类 但同样的想法适用.