ReactJS-「setState」が呼び出されるとレンダーが呼び出されますか?


503

React setStateは、呼び出されるたびにすべてのコンポーネントとサブコンポーネントを再レンダリングしますか?

もしそうなら、なぜですか?私は、状態が変化したときにReactが必要なだけレンダリングするだけだと考えていました。

次の単純な例では、onClickハンドラーが常にをstate同じ値に設定するため、後続のクリックで状態が変化しないにもかかわらず、テキストがクリックされたときに両方のクラスが再度レンダリングされます。

this.setState({'test':'me'});

レンダリングはstateデータが変更された場合にのみ発生することを期待していました。

JS Fiddleとしての例のコードと埋め込みスニペットは次のとおりです。

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

[1]: http://jsfiddle.net/fp2tncmb/2/

同じ問題があり、正確な解決策がわかりません。しかし、私はそれが通常通り機能し始めたコンポーネントから不要なコードをクリーンアップしました。
Jaison

私はあなたの例でそれを指摘したいと思います-あなたの要素がどのように設計されているかは単に一意の状態に依存していないので- setState()ダミーデータで呼び出しても要素が異なるようにレンダリングされるので私はイエスと言います 絶対に何かが変更された可能性がある場合は、オブジェクトを再レンダリングしようとする必要があります。そうでない場合、デモは-意図した動作であると想定して-機能しません。
Tadhg McDonald-Jensen

あなたは正しいかもしれません@ TadhgMcDonald-Jensen-しかし私の理解から、Reactは最初にそれをレンダリングし(状態が何もないものから最初に何かに変化するため)、その後再びレンダリングする必要はありませんでした。もちろん、私は間違っていました。Reactでは、独自のshouldComponentUpdateメソッドを作成する必要があるようです。このメソッドの単純なバージョンは、React自体にすでに含まれている必要があると想定しました。reactに含まれるデフォルトのバージョンのように聞こえるのは単に返すだけでtrue、コンポーネントを毎回強制的に再レン​​ダリングします。
Brad Parks

はい。ただし、仮想DOMで再レンダリングする必要があるだけで、コンポーネントのレンダリングが異なる場合にのみ実際のDOMを変更します。通常、仮想DOMへの更新は無視できます(少なくとも実際の画面での変更と比較して)。更新が必要になるたびにrenderを呼び出してから、変更が発生していないことを確認すると、同じようにレンダリングする必要があると想定するよりもコストがかからず安全です。
Tadhg McDonald-Jensen

回答:


570

Reactは、setStateが呼び出されるたびにすべてのコンポーネントとサブコンポーネントを再レンダリングしますか?

デフォルト-はい。

方法があるshouldComponentUpdateブールは、(オブジェクトnextProps、オブジェクトnextState)各コンポーネントは、このメソッドを持っており、それを判断するために責任だ、「必要があるコンポーネントのアップデートを(実行はレンダリング機能)?」状態を変更するか、親コンポーネントから新しい小道具を渡すたびに。

コンポーネントのshouldComponentUpdateメソッドの独自の実装を作成できますが、デフォルトの実装は常にtrueを返します。つまり、常にレンダリング関数を再実行します。

公式ドキュメントからの引用http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

デフォルトでは、shouldComponentUpdateは常にtrueを返し、状態が変更されたときに微妙なバグを防ぎますが、常に状態を不変として扱い、properとrender()の状態から読み取り専用にする場合は、shouldComponentUpdateを次のようにオーバーライドできます。古い小道具と状態をそれらの代替品と比較する実装。

あなたの質問の次の部分:

もしそうなら、なぜですか?私は、状態が変化したときに、Reactが必要なだけレンダリングするだけだと考えていました。

「レンダー」と呼ばれるものには2つのステップがあります。

  1. 仮想DOMレンダリング:renderメソッドが呼び出されると、コンポーネントの新しい仮想 DOM 構造が返されます。前述したように、このrenderメソッドはsetState()を呼び出すときに常に呼び出されます。shouldComponentUpdateはデフォルトで常にtrueを返すためです。そのため、デフォルトでは、Reactには最適化はありません。

  2. ネイティブDOMレンダリング:Reactは、仮想DOMで変更された場合にのみ、ブラウザーで実際のDOMノードを変更し、必要なだけ変更します。これは、実際のDOMの変更を最適化し、Reactを高速にする優れたReactの機能です。


47
素晴らしい説明!そして、この場合でもReactを本当に際立たせているのは、仮想DOMが変更されない限り、実際の DOMを変更しないことです。したがって、レンダー関数が同じことを2回続けて返す場合、実際のDOMはまったく変更されません...ありがとう!
Brad Parks

1
@tungdは正しいと思います-以下の彼の答えを見てください。これは、コンポーネント間の親子関係の問題でもあります。たとえば、TimeInChildMainの兄弟である場合、そのrender()は不必要に呼び出されません。
gvlax 2015年

2
状態に比較的大きなjavascriptオブジェクト(合計プロパティ〜1k)が含まれ、コンポーネントの大きなツリー(合計〜100)としてレンダリングされる場合...設定する前に、レンダリング関数に仮想domを構築させるか、または状態、新しい状態を古い状態と手動で比較しsetState、違いがあることを検出した場合にのみ呼び出しますか?もしそうなら、これを最善に行う方法-json文字列を比較し、オブジェクトハッシュを構築して比較します...?
Vincent Sels 2016

1
@Petrですが、reactは仮想domを再構築しますが、古い仮想domが新しい仮想domと同じである場合、ブラウザーdomは変更されませんね。
Jaskey

3
また、React.PureComponent(reactjs.org/docs/react-api.html#reactpurecomponent)の使用も検討してください。コンポーネントの状態または小道具が実際に変更された場合にのみ更新(再レンダリング)します。ただし、比較は浅いことに注意してください。
討論者2018

105

いいえ、状態が変化しても、Reactはすべてをレンダリングするわけではありません。

  • コンポーネントがダーティ(状態が変化)になると、そのコンポーネントとその子が再レンダリングされます。これは、ある程度、再レンダリングをできるだけ少なくするためです。renderが呼び出されないのは、一部のブランチが別のルートに移動されたときだけです。理論的には、何も再レンダリングする必要はありません。あなたの例でTimeInChildは、はの子コンポーネントでMainあるため、状態がMain変化したときにも再レンダリングされます。

  • Reactは状態データを比較しません。ときにsetState呼び出され、ダーティとしてマークこと成分(意味し、それは、再レンダリングする必要があります)。注意すべき重要な点はrender、コンポーネントのメソッドが呼び出されても、出力が現在のDOMツリー(仮想DOMツリーとドキュメントのDOMツリーの違い)と異なる場合にのみ、実際のDOMが更新されることです。あなたの例では、stateデータは変更されていませんが、最後の変更時刻が変更されているため、仮想DOMはドキュメントのDOMと異なり、HTMLが更新されます。


はい、これが正解です。実験として、最後の行をReact.renderComponent(<div> <Main /> <TimeInChild /> </ div>、document.body)として変更します。そして取り外し<TimeInChild />の本体からレンダリング()メインコンポーネント。)(レンダリングTimeInChildそれはの子ではないので、デフォルトでは呼び出されませんメイン、それ以上。
gvlax 2015年

おかげで、このようなものは少しトリッキーです。そのため、Reactの作成者は、render()メソッドが「純粋」である必要があることを推奨しました-外部の状態とは無関係です。
2015年

3
@tungd、それはどういう意味some branch is moved to another rootですか?何といいますbranchか?何といいますrootか?
グリーン

2
正解としてマークされた答えが本当に好きです。ネイティブ側、「リアル」側での「レンダリング」の解釈を理解しました...しかし、それを反応ネイティブ側から見ると、もう一度レンダリングすると言う必要があります。幸いなことに、何が実際に変更されたかを判断し、これらの更新のみを行うのに十分なほどスマートです。この答えは、新しいユーザーを混乱させる可能性があります。最初は「いいえ」と言い、次にレンダリングが行われると説明します...
WiRa

1
@tungdはグリーンの質問を説明してくれませんかwhat does it mean some branch is moved to another root? What do you call branch? What do you call root?
madhu131313 '20

7

ここでは他の多くの回答に記載されていますが、コンポーネントは次のいずれかである必要があります。

  • shouldComponentUpdate状態またはプロパティが変更されたときにのみレンダリングするように実装する

  • PureComponentの拡張に切り替えshouldComponentUpdateます。これは、浅い比較のために内部でメソッドをすでに実装しています。

を使用した例を次にshouldComponentUpdate示します。これは、この単純な使用例とデモ目的でのみ機能します。これを使用すると、コンポーネントはクリックするたびに再レンダリングされなくなり、最初に表示されたときと、一度クリックされた後にレンダリングされます。

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    shouldComponentUpdate: function(nextProps, nextState) {
      if (this.state == null)
        return true;
  
      if (this.state.test == nextState.test)
        return false;
        
      return true;
  },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>


6

はい。「shouldComponentUpdate」がfalseを返す場合、代わりにsetStateを呼び出すたびにrender()メソッドを呼び出します。


7
より詳しく説明してください
Karthick Ramesh

3

「更新が失われた」もう1つの理由は次の場合があります。

  • 静的なgetDerivedStateFromPropsが定義されている場合、公式ドキュメントhttps://reactjs.org/docs/react-component.html#updatingに従って、すべての更新プロセスで再実行されます
  • その状態値が最初に小道具から来ている場合、すべての更新で上書きされます。

それが問題である場合、Uは更新中に状態の設定を回避できます。このように状態パラメーター値を確認する必要があります

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}

別の解決策は、初期化されたプロパティを状態に追加し、最初に設定することです(状態がnull以外の値に初期化されている場合)。


0

すべてのコンポーネントではありません。

state全体APPの状態の滝の源のようなコンポーネントルックスインチ

したがって、変更はsetStateが呼び出された場所から発生します。rendersそこから木が呼ばれる。純粋なコンポーネントを使用している場合renderは、スキップされます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.