Reactコンポーネントに強制的に再マウントする方法は?


103

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

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本当だとしましょう。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'と 'unmployment-reason'の比較に失敗したようです。

私が間違っていることを教えてください...


1
data-reactidのそれぞれにMyInput(またはinput、DOMに見られるように)前と後の要素employedスイッチ?
Chris

@Chris MyInput-componentがでラップされた入力をレンダリングするように指定できませんでした<div>data-reactid属性にはラッピングのdivと入力フィールドの両方で異なるようです。job-titleinputはid data-reactid=".0.1.1.0.1.0.1"unemployment-reason取得しますが、inputはidを取得しますdata-reactid=".0.1.1.0.1.2.1"
o01

1
そして、どうunemployment-durationですか?
Chris

@クリス申し訳ありませんが、私はあまりにも早く話しました。(「差分くれ」スパンなし)最初の例ではreactid、属性は、上同一であり、job-titleそしてunemployment-reason、彼らしている別の第二の例では(差分スパンで)ながら。
o01 2016年

以下のため@クリス属性は常にユニークです。unemployment-durationreactid
o01 2016年

回答:


108

おそらく起こっているのは、レンダーの間に追加されるのは1つMyInputunemployment-duration)だけであるとReactが考えていることです。そのため、job-titleはに置き換えられませんunemployment-reason。これは、事前定義された値が交換される理由でもあります。

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

あなたが提供する最後のコードスニペットが機能する理由は、Reactは基本的に親の下のすべての要素の階層を変更する必要があるためdivです。をspan上部ではなく下部に追加した場合、先行する要素の階層は変更されず、それらの要素は再レンダリングされません(そして問題は解決しません)。

Reactの公式ドキュメントには次のように記載されています。

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

Reactがキー付きの子を調整するとき、キーを持つすべての子が(壊滅的ではなく)再配列されるか、または(再利用される代わりに)破棄されることが保証されます。

keydivまたはすべての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使用した非推奨警告に対する私の回答を参照してください。


10
it will determine which components are new and which are old based on their key property.-だった...私の理解するための鍵...
ラリー・

1
本当にありがとうございました !また、マップのすべての子コンポーネントが正常に再レンダリングされ、その理由を理解できなかったこともわかりました。
ValentinVoilean

良いヒントは、divのキーとして状態変数(たとえば、idのように変化する)を使用することです!
Macilias

200

コンポーネントのキーを変更します。

<Component key="1" />
<Component key="2" />

キーが変更されたため、コンポーネントがアンマウントされ、コンポーネントの新しいインスタンスがマウントされます。

編集あなたに文書化されているおそらく派生状態は必要ありません

キーが変更されると、Reactは現在のインスタンスを更新するのではなく、新しいコンポーネントインスタンスを作成します。キーは通常、動的リストに使用されますが、ここでも役立ちます。


45
これは、Reactのドキュメントでもっと明白なはずです。これは私が求めていたものを正確に達成しましたが、見つけるのに何時間もかかりました。
ポール

4
まあこれは私に大いに役立ちました!ありがとう!CSSアニメーションをリセットする必要がありましたが、これを行う唯一の方法は再レンダリングを強制することです。これは、reactのドキュメントでもっと明白になるはずであることに同意します
Alex。P.

@ Alex.P。いいね!CSSアニメーションは時々扱いにくい場合があります。例を見てみたいと思います。
Alex K

1
この技術を記述するドキュメント反応:reactjs.org/blog/2018/06/07/...を
GameSalutes

ありがとうございました。私はこれにとても感謝しています。「randomNumber」のような宣言されていない小道具に乱数を送ってみましたが、機能した唯一の小道具が鍵でした。
ShameWare

5

setStateビューで使用してemployed、状態のプロパティを変更します。これは、Reactレンダリングエンジンの例です。

 someFunctionWhichChangeParamEmployed(isEmployed) {
      this.setState({
          employed: isEmployed
      });
 }

 getInitialState() {
      return {
          employed: true
      }
 },

 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>
        );
    }
}

私はすでにこれをやっています。'employed'変数はビューコンポーネントの状態の一部であり、setState関数によって変更されます。説明しなくてすみません。
o01 2016年

「雇用された」変数は、React状態のプロパティである必要があります。コード例では明らかではありません。
Dmitriy 2016年

this.state.employedをどのように変更しますか?コードを見せてください。コードの適切な場所でsetStateを使用してもよろしいですか?
Dmitriy 2016年

ビューコンポーネントは、私の例が示すよりも少し複雑です。MyInput-componentsに加えて、onChangeハンドラーで「employed」の値を制御するラジオボタングループもレンダリングします。onChangeハンドラーで、ビューコンポーネントの状態を適宜設定します。ラジオボタングループの値が変更されると、ビューは再レンダリングされますが、Reactは最初のMyInput-componentが「employed」が変更される前と同じであると考えています。
o01 2016年

0

アプリのCrudに取り組んでいます。これは、依存関係としてReactstrapを取得した方法です。

import React, { useState, setState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import firebase from 'firebase';
// import { LifeCrud } from '../CRUD/Crud';
import { Row, Card, Col, Button } from 'reactstrap';
import InsuranceActionInput from '../CRUD/InsuranceActionInput';

const LifeActionCreate = () => {
  let [newLifeActionLabel, setNewLifeActionLabel] = React.useState();

  const onCreate = e => {
    const db = firebase.firestore();

    db.collection('actions').add({
      label: newLifeActionLabel
    });
    alert('New Life Insurance Added');
    setNewLifeActionLabel('');
  };

  return (
    <Card style={{ padding: '15px' }}>
      <form onSubmit={onCreate}>
        <label>Name</label>
        <input
          value={newLifeActionLabel}
          onChange={e => {
            setNewLifeActionLabel(e.target.value);
          }}
          placeholder={'Name'}
        />

        <Button onClick={onCreate}>Create</Button>
      </form>
    </Card>
  );
};

そこにいくつかの反応フック


SOへようこそ!あなたは良い答えを書く方法を確認したいかもしれません。コードを投稿するだけでなく、質問をどのように解決したかに説明を追加します。
displacedtexan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.