1. ホーム
  2. javascript

[解決済み] Reactコンポーネントを強制的に再マウントするには?

2022-06-05 06:27:18

質問

条件付きレンダーを持つビューコンポーネントがあるとします。

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

MyInputはこのような感じです。

class MyInput extends React.Component {

    ...

    render(){
        return (
            <div>
                <input name={this.props.name} 
                    ref="input" 
                    type="text" 
                    value={this.props.value || null}
                    onBlur={this.handleBlur.bind(this)}
                    onChange={this.handleTyping.bind(this)} />
            </div>
        );
    }
}

例えば employed が true であるとします。これを false に切り替えて他のビューをレンダリングすると、常に unemployment-duration だけが再初期化されます。また unemployment-reason からの値でプリフィルされます。 job-title (の値で埋められる(条件が変わる前に値が与えられていた場合)。

2番目のレンダリングルーチンのマークアップを次のように変更すると。

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <span>Diff me!</span>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

すべて正常に動作しているように見えます。Reactが'job-title'と'unemployment-reason'のdiffに失敗しているだけのように見えますが。

何が間違っているのか教えてください...。

どのように解決するのですか?

おそらく起こっていることは、Reactが1つだけの MyInput ( unemployment-duration )がレンダリングの間に追加されます。そのため job-title に置き換えられることはありません。 unemployment-reason に置き換えられることはなく、それが定義済みの値が入れ替わる理由でもあります。

React が diff を実行するとき、どのコンポーネントが新しく、どのコンポーネントが古いかをその key プロパティに基づいて、新しいコンポーネントと古いコンポーネントを決定します。そのようなキーがコードで提供されていない場合、独自に生成されます。

提供された最後のコードスニペットが機能する理由は、React が本質的に親 div の下にあるすべての要素の階層を変更する必要があり、それがすべての子の再レンダリングをトリガーすると思うからです (これが動作する理由です)。もしあなたが span を追加していた場合、前の要素の階層は変更されず、それらの要素は再レンダリングされません (そして問題は解決されません)。

以下は、公式の React ドキュメント に書いてあります。

子要素が入れ替わる場合 (検索結果の場合)、または新しいコンポーネントがリストの先頭に追加される場合 (ストリームの場合) は、状況がさらに複雑になります。レンダー パスにまたがって各子供の ID と状態を維持する必要があるこれらのケースでは、キーを割り当てることにより、各子供を一意に識別できます。

React がキーを持つ子を調整するとき、キーを持つ子は (クローバーではなく) 並べ替えられるか、(再利用ではなく) 破棄されることを確認します。

これを解決するには、一意の key 要素に、親要素である div またはすべての MyInput 要素に適用されます。

例えば

render(){
    if (this.state.employed) {
        return (
            <div key="employed">
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div key="notEmployed">
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

または

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput key="title" ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" />
                <MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

これで、Reactがdiffを実行すると divs が異なることを認識し、すべての子要素を含めて再レンダリングします (最初の例)。2 番目の例では、差分が成功すると job-titleunemployment-reason は異なるキーを持つようになったので

もちろん、一意であればどんなキーでも使うことができます。


2017年8月更新

Reactでキーがどのように機能するかについてのより良い洞察のために、私は強く私の答えを読むことをお勧めします。 React.jsのユニークキーを理解する .


2017年11月更新

このアップデートは少し前に投稿されたはずなのですが、文字列リテラルを使って ref での文字列リテラル使用は非推奨となりました。例えば ref="job-title" は、代わりに ref={(el) => this.jobTitleRef = el} (のようになります(例)。に対する私の回答を参照してください。 this.refsを使用した非推奨の警告 を参照してください。