支柱をUSENTに入れる



注意:
例は私のブログでインタラクティブですので、あなたはそれを読んでいるより良い経験があるかもしれません:
Putting props to useState
first part UEstate Payfallシリーズの私は、状態のすべてを一緒に状態を回避することについて話しました.
この部分は一般的なシナリオです.ここでは、propsとして得られる値で状態を初期化したいと思います.これは我々がおそらく多くを行う何かであり、それは間違っていないが、それは我々が意識する必要があるいくつかの潜在的な問題があります.


私は古典的なリスト/詳細ユースケースを例として使用します.私たちは人のリストを持っており、それらのいずれかを選択すると記入されて詳細な形式になります.我々は詳細フォームで人のメールアドレスを表示し、そのデータを更新する適用ボタンを持っている.
const persons = [
    {
        id: 1,
        name: 'Dominik',
        email: '[email protected]',
    },
    {
        id: 2,
        name: 'John',
        email: '[email protected]',
    },
]

function App() {
    const [selected, setSelected] = React.useState(persons[0])

    return (
        <div>
            {persons.map((person) => (
                <button type="button" key={person.id} onClick={() => setSelected(person)}>
                    {person.id === selected.id ? person.name.toUpperCase() : person.name}
                </button>
            ))}
            <DetailView initialEmail={selected.email} />
        </div>
    )
}

function DetailView({ initialEmail }) {
    const [email, setEmail] = React.useState(initialEmail)

    return (
        <div>
            <input
                type="text"
                value={email}
                onChange={(event) => setEmail(event.target.value)}
            />
            <button type="button" onClick={() => alert(email)}>
                Apply
            </button>
        </div>
    )
}

USENT初期値
あなたは、例が働いていないとすぐに気がつくかもしれません.あなたは電子メールアドレスを編集して、適用をクリックすることができます、しかし、ジョンをクリックするならば、入力フィールドは更新されません.
反応するだけでなくthink in hooks LifeCycleではなく、それが状態になると、最初のレンダリング(また、マウントとして知られている)とさらにレンダリング(より良いRemenderとして知られている)の間に大きな違いがあります.
usEstateフックの初期値は常に再レンダリングされます.
Johnをクリックすると、DetailViewコンポーネントが再描画されます(画面に既に存在するため)、ジョンのメールが我々の状態に置かれないことを意味します.Bummer、我々はまだメールアドレスを編集するには、ローカル州が必要なので(ドラフト変更を維持する).Person配列を直接更新する必要はありません.
私は、これと同様のユースケースを扱う3つの方法を知っています:

1 .条件付きでDetailViewをレンダリングする
画面に表示されるモダールやその他のコンポーネントを使用しているとき、私たちはこれをたくさんします.
モーダルが通常条件付きでレンダリングされるので、モードでDetailViewを示すことは魔法のように仕事より上に我々のコードを作ります.ジョンをクリックすると、モーダル初期値が尊重されます.ユーザーがモーダルを閉じるとき、それはアンマウントされます、そして、人が選ばれる次の時間は再びそれをつけられます.
以下にその方法を示します.
const persons = [
    {
        id: 1,
        name: 'Dominik',
        email: '[email protected]',
    },
    {
        id: 2,
        name: 'John',
        email: '[email protected]',
    },
]

function App() {
    const [selected, setSelected] = React.useState()

    const close = () => setSelected(undefined)

    return (
        <div>
            {persons.map((person) => (
                <button type="button" key={person.id} onClick={() => setSelected(person)}>
                    {person.name}
                </button>
            ))}
            {selected && (
                <div
                    style={{
                        position: 'fixed',
                        top: '0',
                        left: '0',
                        paddingTop: '100px',
                        width: '100%',
                        height: '100%',
                        backgroundColor: 'rgba(0,0,0,0.4)',
                    }}
                >
                    <div
                        style={{
                            display: 'flex',
                            justifyContent: 'center',
                            width: '80%',
                            height: '50vh',
                            margin: 'auto',
                            backgroundColor: 'white',
                        }}
                    >
                        <DetailView initialEmail={selected.email} close={close} />
                        <span style={{ cursor: 'pointer' }} onClick={close}>
                            &times;
                        </span>
                    </div>
                </div>
            )}
        </div>
    )
}

function DetailView({ initialEmail, close }) {
    const [email, setEmail] = React.useState(initialEmail)

    return (
        <div>
            <input
                type="text"
                value={email}
                onChange={(event) => setEmail(event.target.value)}
            />
            <button
                type="button"
                onClick={() => {
                    alert(email)
                    close()
                }}
            >
                Apply
            </button>
        </div>
    )
}
私のCSSを許してください、私はウェブ開発のこの部分で吸います😅
しかし、例は現在働きます.これは、モードが条件付きで我々のDetailViewをレンダリングするためです.
私は、あなたの多くが確かにそれをしたと確信します、そして、それは有効な解決です.しかし、これはモードでDetailViewをレンダリングしているためだけに動作します.DetailViewをどこでもレンダリング可能にするには、異なるソリューションが必要になります.

起立状態
あなたはおそらく前にこのフレーズを聞いたことがある、公式の反応ドキュメントも持っているa section on that topic .
この例では、基本的にはドラフトの状態を取り、さらにそれを木の上に移動させることを意味します.したがって、完全に制御されたコンポーネントをDetailViewにします.DetailViewは、ローカルの状態を全く必要としないので、小道具を状態にするという問題はありません.
const persons = [
    {
        id: 1,
        name: 'Dominik',
        email: '[email protected]',
    },
    {
        id: 2,
        name: 'John',
        email: '[email protected]',
    },
]

function App() {
    const [selected, setSelected] = React.useState(persons[0])
    const [email, setEmail] = React.useState(selected.email)

    return (
        <div>
            {persons.map((person) => (
                <button
                    type="button"
                    key={person.id}
                    onClick={() => {
                        setSelected(person)
                        setEmail(person.email)
                    }}
                >
                    {person.id === selected.id ? person.name.toUpperCase() : person.name}
                </button>
            ))}
            <DetailView email={email} setEmail={setEmail} />
        </div>
    )
}

function DetailView({ email, setEmail }) {
    return (
        <div>
            <input
                type="text"
                value={email}
                onChange={(event) => setEmail(event.target.value)}
            />
            <button type="button" onClick={() => alert(email)}>
                Apply
            </button>
        </div>
    )
}
今、アプリはすべての状態を完全に制御しており、DetailViewはいわゆる“ダムコンポーネント”です.このアプローチは多くのユースケースで実現可能ですが、欠点はありません.
入力フィールドの入力はすべてのキーストロークと全体のアプリケーションを再レンダリングします.これはこの小さな例のための問題ではないが、それは大きなアプリケーションのための問題かもしれない.人々は、効率的に再描画することを約束しているので、世界的な州のマネージャーによく頼ります.
一度、電子メール状態の草案の範囲が現在あまりに大きいと主張することもできました.なぜ、アプリはそれについても気にする、それはおそらく、ユーザーが適用されれば、新しい電子メールについて気にする.
第3のアプローチは2つの間の中間の地面の一種である.

3 .キーで完全に制御できない
const persons = [
    {
        id: 1,
        name: 'Dominik',
        email: '[email protected]',
    },
    {
        id: 2,
        name: 'John',
        email: '[email protected]',
    },
]

function App() {
    const [selected, setSelected] = React.useState(persons[0])

    return (
        <div>
            {persons.map((person) => (
                <button type="button" key={person.id} onClick={() => setSelected(person)}>
                    {person.id === selected.id ? person.name.toUpperCase() : person.name}
                </button>
            ))}
            <DetailView key={selected.id} initialEmail={selected.email} />
        </div>
    )
}

function DetailView({ initialEmail }) {
    const [email, setEmail] = React.useState(initialEmail)

    return (
        <div>
            <input
                type="text"
                value={email}
                onChange={(event) => setEmail(event.target.value)}
            />
            <button type="button" onClick={() => alert(email)}>
                Apply
            </button>
        </div>
    )
}
これは最初の例と同じコードです.
- <DetailView initialEmail={selected.email} />
+ <DetailView key={selected.id} initialEmail={selected.email} />

反応キー
The key 反応コンポーネントの属性は特別なものです.キーはほとんど反応のための安定性を意味するリストのために使用されます.
そのため、リコンセプターはどの要素を再利用することができるかを知っています.
しかし、あなたはどんな反応をするかについて、どんな成分にでも重要な属性を置くこともできます.
これは依存関係の配列のように少し効果があります.それが変わるならば、前のRenderに比べて、反応はコンポーネントの「取り付け」を再実行します.
もっと知りたい方はこちらをご覧くださいexplanation about reconciliation .

非解決策
この問題を解決するように誘惑されるかもしれません.
function DetailView({ initialEmail }) {
    const [email, setEmail] = React.useState(initialEmail)

    React.useEffect(() => {
        setEmail(initialEmail)
    }, [initialEmail])

    return (...)
}

私はこれらの一般的に反パターンのような効果を考慮します.効果が同期のために使われるならば、彼らは反応の外で何か反応している状態を同期させるのに用いられなければなりません.
しかし、ここでは、我々はすでに反応状態と反応中に住んでいる何かを同期されます.さらに、我々が同期する条件は、我々が達成したいものを本当に反映しません:我々は、もう一つの人が選ばれるときはいつでも、必ず電子メールが変わるとき、状態をリセットしたいです.
最初の解決は条件付きレンダリングを通してこれをします、人を選ぶボタンがクリックされるとき、はっきりしたキー(選択された人のID)を提供することによって、第3のものを明示的にセットすることによる第2のもの.
彼らは一般的にもユニークですが、しかし、2人が同じデータ(例えばfirstname)を持つならば、電子メールは準最適例であるかもしれません?私たちは別の人をクリックしても効果が再実行されませんので、ドラフト状態はリセットされません.
同様に、親コンポーネントのデータが変更された場合(例えば、react-query ), しかし、我々のユーザーはすでに入力フィールドの値を変更しましたか?これらのケースでユーザー入力を本当にオーバーライドしますか?
だから、これらのような効果は、あなたがより良いだろうコーナーケースのエラーを追跡するの束にあなたを開く.

持ち帰り
個人的には、私は好ましい解決策を持っていません.私は3つのアプローチを時折使用しました.
ドラフト状態を所有している詳細ビューはいくつかの利点を持っていますが、アンマウントは少しのコストで来ます、そして、コンポーネントがリセットされるべきであるとき、あなたは常に安定したキーまたは明確な徴候を持っていません.
完全に制御された構成要素が通常理由をより簡単にするように、状態を上げることは利点を持ちます、しかし、それは常に大きなアプリケーションで容易に避けられないかもしれません.
何を決定してください、同期状態を使用しないでください“ソリューション”.私にとって、このアプローチは古いコンポーネントのReceiveProps LifeCycleと類似しています.私は、うまく終わることを思い出しません.Here は2018年からこの記事に非常に良い記事です.
どちらの解決方法がお好きですか.コメントを残す⬇️