setStateを呼び出さずにReactコンポーネントを強制的に再レン​​ダリングできますか?


688

変更をリッスンしたい外部(コンポーネント)の監視可能なオブジェクトがあります。オブジェクトが更新されると、変更イベントが発生し、変更が検出されたときにコンポーネントを再レンダリングします。

トップレベルではReact.renderこれが可能でしたが、コンポーネント内では機能しません(renderメソッドがオブジェクトを返すだけなので、これは理にかなっています)。

次にコード例を示します。

export default class MyComponent extends React.Component {

  handleButtonClick() {
    this.render();
  }

  render() {
    return (
      <div>
        {Math.random()}
        <button onClick={this.handleButtonClick.bind(this)}>
          Click me
        </button>
      </div>
    )
  }
}

ボタンをクリックすると内部的にが呼び出されますがthis.render()、それが実際にレンダリングを発生させる原因ではありません(によって作成されたテキスト{Math.random()}は変更されないため、実際にこれを確認できます)。ただし、のthis.setState()代わりに単に呼び出すとthis.render()、正常に機能します。

だから私は私の質問だと思います: Reactコンポーネントは再レンダリングするために状態を持つ必要がありますか?状態を変更せずにコンポーネントをオンデマンドで更新する方法はありますか?


7
受け入れ答えと言うには、this.forceUpdate()すべての答えと、いくつかのコメントの残りの部分は使用して反対しているのに対し、適切なソリューションですforceUpdate()。それでは、質問はまだ適切な解決策/答えを得ていなかったと言っても大丈夫でしょうか?
adi

6
除外された回答が当時の私の質問に答えました。技術的には私が探していた答えですが、それでも正しい答えだと思います。私が思う他の答えは、同じ質問を知っている人が知っておくべき良い補足情報です。
フィリップウォルトン

興味深いことに、プレーンオブジェクトに初期化する以外は何も状態を必要とせず、this.setState({})を呼び出すだけで新しいレンダリングがトリガーされます。反応は素晴らしいですが、奇妙なこともあります。したがって、追加の配管をせずに変更をトリガーしたり、コンポーネントインスタンスごとのデータを心配したりせずに、店舗データを直接ループできます。
Jason Sebring

総じて私はイエスと言うでしょう。強制更新を使用している場合、これは、コンポーネントの更新がアプリの状態管理以外の変更に依存している可能性がある場合に使用します。その良い例は思いつきません。知っておくと便利です。
ダニエル

回答:


765

コンポーネントでは、呼び出しthis.forceUpdate()て再レンダリングを強制できます。

ドキュメント:https : //facebook.github.io/react/docs/component-api.html


168
別の方法はthis.setState(this.state);です。
2016

25
forceupdateの使用はお勧めしません。最後の回答を参照してください。
Varand Pezeshkian 2016

42
一方でthis.forceUpdate()askersかの問題に非常に良い解決策ではない、それはタイトルに提起質問に対する正しい答えです。通常は回避する必要がありますが、forceUpdateが適切なソリューションである場合があります。
ArneHugo 2016年

8
@kar実際には、shouldComponentUpdate()ソリューションを無駄にするために追加のロジックを実装できます。
Qwerty

4
最大コールスタックサイズを超えました
IntoTheDeep

326

forceUpdateReactの考え方から逸脱しているため、避けてください。Reactのドキュメントは、いつforceUpdate使用されるかの例を引用しています。

デフォルトでは、コンポーネントの状態または小道具が変更されると、コンポーネントは再レンダリングされます。ただし、これらが暗黙的に変更される場合(たとえば、オブジェクト自体を変更せずにオブジェクト内の深いデータが変更される場合)、またはrender()メソッドが他のデータに依存している場合は、次の呼び出しでrender()を再実行する必要があることをReactに通知できます。アップデートさせる()。

ただし、オブジェクトが深くネストされている場合でも、 forceUpdate不要で。不変のデータソースを使用することで、変更の追跡が簡単になります。変更すると常に新しいオブジェクトが生成されるため、オブジェクトへの参照が変更されたかどうかを確認するだけで済みます。ライブラリImmutable JSを使用して、不変データオブジェクトをアプリに実装できます。

通常は、forceUpdate()のすべての使用を避け、render()のthis.propsおよびthis.stateからのみ読み取るようにしてください。これにより、コンポーネントが「純粋」になり、アプリケーションがよりシンプルで効率的になります。アップデートさせる()

再レンダリングしたい要素のキーを変更しても機能します。状態を介して要素にキープロップを設定し、次にセット状態を更新して新しいキーを取得する場合。

<Element key={this.state.key} /> 

次に変更が発生し、キーをリセットします

this.setState({ key: Math.random() });

これにより、キーが変更されている要素が置き換えられます。これが役立つ例として、画像のアップロード後にリセットしたいファイル入力フィールドがある場合があります。

OPの質問に対する本当の答えは forceUpdate()このソリューションがさまざまな状況で役立つことがわかった。また、使用しforceUpdateていることに気付いた場合は、コードを確認して、別の方法があるかどうかを確認することもできます。

注1-9-2019:

上記(キーの変更)は、要素を完全に置き換えます。変更を加えるためにキーを更新していることに気付いた場合は、おそらくコードの他の場所に問題があります。Math.random()キーを使用すると、レンダリングごとに要素が再作成されます。このようにキーを更新することはお勧めしません。reactはキーを使用して、再レンダリングするための最良の方法を決定するためです。


16
なぜこれが反対票を投じたのか知りたいのですが?私はスクリーンリーダーにariaアラートに応答させるために頭を叩いてきましたが、これが確実に機能する唯一のテクニックです。クリックされるたびに同じアラートを生成するUIに何かがある場合、デフォルトでは、反応はDOMを再レンダリングしないため、スクリーンリーダーは変更を通知しません。一意のキーを設定すると機能します。たぶんそれはまだ状態を設定することを含んでいるからです しかし、キーを設定してDOMへの再レンダリングを強制することは非常に重要です。
Martin Bayly 2016年

2
-1:forceupdate()の使用はお勧めできません。2:この方法は、npmを介してインストールしたサードパーティのコンポーネント、つまり独自のコンポーネントではない場合に非常に役立ちます。たとえば、React-resizable-and-movableは私が使用するサードパーティのコンポーネントですが、私のコンポーネントのsetstateに反応しません。これは素晴らしいソリューションです。
Varand Pezeshkian 2016

80
これはハックであり、の乱用ですkey。まず、その意図は不明です。コードの読者は、keyこの方法を使用する理由を理解するために一生懸命努力する必要があります。第二に、これはほど純粋ではありませんforceUpdate。「純粋な」Reactは、コンポーネントの視覚的表現がその状態に100%依存していることを意味します。したがって、状態を変更すると、コンポーネントは更新されます。ただし、深くネストされたオブジェクトがある場合(forceUpdateドキュメントがそれを使用する理由を引用するシナリオ)、使用forceUpdateするとそれが明確になります。第三にMath.random()…ランダムです。理論的には、同じ乱数を生成できます。
アトムカーク2016

8
@xtraSimplicityこれがReactのやり方に準拠していることに同意できません。forceUpdateこれを行うReactの方法です。
ロブ・グラント

3
@ロバートグラント、振り返って、私は同意します。追加された複雑さはそれだけの価値はありません。
XtraSimplicity 2017

80

実際には、forceUpdate()としてだけ正解であるsetState()かもしれないが、ない追加のロジックが実装されている場合、再レンダリングトリガーshouldComponentUpdate()またはそれは単に返すときfalse

アップデートさせる()

を呼び出すforceUpdate()render()、コンポーネントが呼び出され、はスキップされshouldComponentUpdate()ます。もっと...

setState()

setState()に条件付きレンダリングロジックが実装されていない限り、は常に再レンダリングをトリガーしshouldComponentUpdate()ます。もっと...


forceUpdate() コンポーネントの内部から呼び出すことができます this.forceUpdate()


フック:Reactでフックを使用してコンポーネントを強制的に再レン​​ダリングするにはどうすればよいですか?


ところで:あなたは状態を変えていますか、あなたの入れ子になったプロパティは伝播しませんか?


1
注意すべき興味深い点の1つは、thisState.foo = 'bar'などのsetStateの外部での状態の割り当ては、レンダーライフサイクルメソッドをトリガーしないということです
lfender6445

4
@ lfender6445コンストラクタのthis.state外部で直接状態を割り当てると、エラーもスローされます。2016年にはそうしなかったかもしれません。しかし、私はそれが今それを行うとかなり確信しています。
タイラー

直接の状態割り当てを回避するためのリンクをいくつか追加しました。
Qwerty

36

forceUpdate次のようにして回避した

WRONG WAY:インデックスをキーとして使用しないでください

this.state.rows.map((item, index) =>
   <MyComponent cell={item} key={index} />
)

正しい方法:データIDをキーとして使用します。

this.state.rows.map((item) =>
   <MyComponent item={item} key={item.id} />
)

このようなコードの改善を行うことにより、コンポーネントは一意になり、自然にレンダリングされます


this.state.rows.map((item) => <MyComponent item={item} key={item.id} /> )
タル

私を正しい方向に向けてくれてありがとう。インデックスをキーとして使用することは、本当に私の問題でした。
RoiEX

私の反対票を正当化するために、このアプローチを使用することは良い習慣ですが、この回答は質問とは関係ありません。
micnic

34

関係(親子)に拘束されない2つのReactコンポーネントを通信する場合は、Fluxを使用することをお勧めしますまたは同様のアーキテクチャー。

あなたがしたいことは、モデルとそのインターフェースを保持する監視可能なコンポーネントストアの変更をリッスンし、レンダリングを変更する原因となるデータを保存することですstate中をMyComponent。ストアが新しいデータをプッシュすると、コンポーネントの状態が変更され、自動的にレンダリングがトリガーされます。

通常、の使用は避けてくださいforceUpdate()。ドキュメントから:

通常は、forceUpdate()のすべての使用を避け、render()のthis.propsおよびthis.stateからのみ読み取るようにしてください。これにより、アプリケーションがよりシンプルで効率的になります


2
コンポーネントの状態のメモリステータスは何ですか?ページに5つのコンポーネントがあり、それらすべてが1つのストアをリッスンしている場合、データをメモリに5回持っていますか、それとも参照ですか?そして、それらのリスナーのすべてが速く足し合わないのですか?単にターゲットにデータを渡すよりも「トリクルダウン」する方が良いのはなぜですか?
AJFarkas

5
実際には、ストアデータをコンポーネントの小道具に渡し、コンポーネントの状態をスクロール状態やその他のマイナーなui固有のものにのみ使用することをお勧めします。あなたが使用している場合はReduxのを(私はそれをお勧めします)、あなたは使用することができ、接続の機能をreact-reduxユーザーが指定するマッピング関数に基づいて、必要な時にコンポーネント小道具に店舗状態をマップに自動的に。
Stijn de Witt 2016

1
@AJFarkas状態はストアのデータに割り当てられます。これはとにかくそれへのポインターに過ぎないため、クローンを作成しない限り、メモリは問題になりません。
Jason Sebring

4
「forceUpdate()は常に回避する必要があります」は正しくありません。ドキュメントには、「通常、forceUpdate()のすべての使用を回避するようにしてください」と明記されています。forceUpdate
AJPの

2
@AJPあなたは完全に正しいです、それは個人的な偏見でした:)私は答えを編集しました。
gcedo 2017年

18

フックまたはHOCを使用して選択してください

使用フック又はHOC(高次成分)パターン、あなたの店舗が変更されたとき、あなたは自動更新を持つことができます。これはフレームワークなしの非常に軽量なアプローチです。

ストアの更新を処理するuseStoreフックの方法

interface ISimpleStore {
  on: (ev: string, fn: () => void) => void;
  off: (ev: string, fn: () => void) => void;
}

export default function useStore<T extends ISimpleStore>(store: T) {
  const [storeState, setStoreState] = useState({store});
  useEffect(() => {
    const onChange = () => {
      setStoreState({store});
    }
    store.on('change', onChange);
    return () => {
      store.off('change', onChange);
    }
  }, []);
  return storeState.store;
}

withStores HOCはストア更新を処理します

export default function (...stores: SimpleStore[]) {
  return function (WrappedComponent: React.ComponentType<any>) {
    return class WithStore extends PureComponent<{}, {lastUpdated: number}> {
      constructor(props: React.ComponentProps<any>) {
        super(props);
        this.state = {
          lastUpdated: Date.now(),
        };
        this.stores = stores;
      }

      private stores?: SimpleStore[];

      private onChange = () => {
        this.setState({lastUpdated: Date.now()});
      };

      componentDidMount = () => {
        this.stores &&
          this.stores.forEach((store) => {
            // each store has a common change event to subscribe to
            store.on('change', this.onChange);
          });
      };

      componentWillUnmount = () => {
        this.stores &&
          this.stores.forEach((store) => {
            store.off('change', this.onChange);
          });
      };

      render() {
        return (
          <WrappedComponent
            lastUpdated={this.state.lastUpdated}
            {...this.props}
          />
        );
      }
    };
  };
}

SimpleStoreクラス

import AsyncStorage from '@react-native-community/async-storage';
import ee, {Emitter} from 'event-emitter';

interface SimpleStoreArgs {
  key?: string;
  defaultState?: {[key: string]: any};
}

export default class SimpleStore {
  constructor({key, defaultState}: SimpleStoreArgs) {
    if (key) {
      this.key = key;
      // hydrate here if you want w/ localState or AsyncStorage
    }
    if (defaultState) {
      this._state = {...defaultState, loaded: false};
    } else {
      this._state = {loaded: true};
    }
  }
  protected key: string = '';
  protected _state: {[key: string]: any} = {};
  protected eventEmitter: Emitter = ee({});
  public setState(newState: {[key: string]: any}) {
    this._state = {...this._state, ...newState};
    this.eventEmitter.emit('change');
    if (this.key) {
      // store on client w/ localState or AsyncStorage
    }
  }
  public get state() {
    return this._state;
  }
  public on(ev: string, fn:() => void) {
    this.eventEmitter.on(ev, fn);
  }
  public off(ev: string, fn:() => void) {
    this.eventEmitter.off(ev, fn);
  }
  public get loaded(): boolean {
    return !!this._state.loaded;
  }
}

使い方

フックの場合:

// use inside function like so
const someState = useStore(myStore);
someState.myProp = 'something';

HOCの場合:

// inside your code get/set your store and stuff just updates
const val = myStore.myProp;
myOtherStore.myProp = 'something';
// return your wrapped component like so
export default withStores(myStore)(MyComponent);

確認してください そのような地球変動の利益を得るためにシングルトンとして、あなたの店舗をエクスポートするには:

class MyStore extends SimpleStore {
  public get someProp() {
    return this._state.someProp || '';
  }
  public set someProp(value: string) {
    this.setState({...this._state, someProp: value});
  }
}
// this is a singleton
const myStore = new MyStore();
export {myStore};

このアプローチは非常にシンプルで、私にとってはうまくいきます。また、私は大規模なチームで作業し、ReduxとMobXを使用していますが、それらも同様に優れていますが、定型文はたくさんあります。個人的には、自分のアプローチが好きです。必要なときに簡単にできるようなコードをたくさん嫌うからです。


2
次のようにメソッドの最初の行を記述する必要があるupdateメソッドのStoreクラスの例にタイプミスがあると思いますthis._data = {...this._data, ...newData}
Norbert、

2
Reactは構成を優先して継承を阻止します。reactjs.org/docs/composition-vs-inheritance.html
フレッド

1
わかりやすさ/読みやすさについて疑問に思っているだけですが、this.setState(this.state)はthis.forceUpdate()よりも優れていますか?
ゾマン

17

だから私は私の質問だと思います:Reactコンポーネントは再レンダリングするために状態を持つ必要がありますか?状態を変更せずにコンポーネントをオンデマンドで更新する方法はありますか?

他の答えはあなたができる方法を説明しようとしましたが、要点はあなたがすべきではないということです。キーを変更するハックなソリューションでさえ、要点を逃しています。Reactの力は、何かをいつレンダリングするかを手動で管理することの制御を放棄することです。代わりに、何かが入力にどのようにマップすべきかについて自分自身を心配するだけです。次に、入力ストリームを供給します。

手動で強制的に再レン​​ダリングする必要がある場合は、ほとんど間違いなく正しく動作していません。


@ art-solopov、あなたはどのような問題を参照していますか?どのドキュメントがそれらを提示しますか?状態のネストされたオブジェクトを参照していると仮定すると、答えは、状態を格納するために別の構造を使用することです。それは明らかに、Reactで状態を処理する次善の方法です。また、状態を個別に管理しないでください。状態管理システムを使用しない、Reactの最大の利点の1つを失うことになります。アップデートを手動で管理することは、アンチパターンです。
カイルベイカー

この質問を確認してください。stackoverflow.com/ questions / 61277292 / … ?
HuLu ViCa

4

あなたはそれをいくつかの方法で行うことができます:

1. forceUpdate()メソッドを使用します。

forceUpdate()メソッドを使用するときに発生する可能性のあるいくつかの不具合があります。1つの例は、shouldComponentUpdate()メソッドを無視し、shouldComponentUpdate()falseが返されるかどうかに関係なくビューを再レンダリングすることです。このため、forceUpdate()の使用は可能な限り回避する必要があります。

2. this.stateをsetState()メソッドに渡す

次のコード行は、前の例の問題を克服します。

this.setState(this.state);

実際にこれが行っているのは、現在の状態を現在の状態で上書きして、再レンダリングをトリガーすることだけです。これは必ずしも最善の方法とは限りませんが、forceUpdate()メソッドを使用して発生する可能性のあるいくつかの不具合を克服します。


2
SETSTATEがバッチ処理されているので、それは何をするおそらくほうが安全です:this.setState(prevState => prevState);
マーク・ギャロウェイ

1
それが「グリッチ」である理由が本当にわからない。メソッドの名前( 'forceUpdate')を明確にすることはできません。常に更新を強制します。
ゾマン

4

コンポーネントを再レンダリングするには、いくつかの方法があります。

最も簡単な解決策は、forceUpdate()メソッドを使用することです。

this.forceUpdate()

もう1つの解決策は、state(nonUsedKey)で未使用のキーを作成し、このnonUsedKeyを更新してsetState関数を呼び出すことです。

this.setState({ nonUsedKey: Date.now() } );

または、現在の状態をすべて書き換えます。

this.setState(this.state);

小道具の変更もコンポーネントの再レンダリングを提供します。


3

完全を期すために、これを機能コンポーネントで実現することもできます。

const [, updateState] = useState();
const forceUpdate = useCallback(() => updateState({}), []);
// ...
forceUpdate();

または、再利用可能なフックとして:

const useForceUpdate = () => {
  const [, updateState] = useState();
  return useCallback(() => updateState({}), []);
}
// const forceUpdate = useForceUpdate();

参照:https : //stackoverflow.com/a/53215514/2692307

force-updateメカニズムを使用することは、反応の考え方に反するため、依然として悪い習慣であることに注意してください。したがって、可能であれば、それを回避する必要があります。


2

this.forceUpdate()を以下のように使用できます。

       class MyComponent extends React.Component {



      handleButtonClick = ()=>{
          this.forceUpdate();
     }


 render() {

   return (
     <div>
      {Math.random()}
        <button  onClick={this.handleButtonClick}>
        Click me
        </button>
     </div>
    )
  }
}

 ReactDOM.render(<MyComponent /> , mountNode);

DOMの要素「Math.random」の部分は、setStateを使用してコンポーネントを再レンダリングした場合でも更新されます。

ここでのすべての答えは、理解のための質問を補足する正しいものです。setState({})を使用せずにコンポーネントを再レンダリングすることはわかっているので、forceUpdate()を使用します。

上記のコードは、以下のようにsetStateで実行されます。

 class MyComponent extends React.Component {



             handleButtonClick = ()=>{
                this.setState({ });
              }


        render() {
         return (
  <div>
    {Math.random()}
    <button  onClick={this.handleButtonClick}>
      Click me
    </button>
  </div>
)
  }
 }

ReactDOM.render(<MyComponent /> , mountNode);

1

受け入れられた回答をバックアップするための別の返信:-)

forceUpdate()彼らは通常、関数型プログラミングに対して非常に「これがそれを行う唯一の方法」のアプローチを持っているため、Reactの使用を推奨しません。これは多くの場合問題ありませんが、多くのReact開発者はオブジェクト指向のバックグラウンドを備えており、そのアプローチでは、監視可能なオブジェクトをリッスンしてもまったく問題ありません。

そして、もしそうなら、あなたはおそらくあなたが観察可能な「火」のときに再レンダリングしなければならないことを知っているでしょう、そしてそれで、あなたは使用すべきでforceUpdate()あり、それは実際にはそれのプラスですshouldComponentUpdate()ここは関与しないです。

OOアプローチを取るMobXのようなツールは、実際には表面下でこれを行っています(実際にはMobXがrender()直接呼び出します)。


0

forceUpdate()を回避するのが最善です。再レンダリングを強制する1つの方法は、一時的な外部変数にrender()の依存関係を追加し、必要に応じてその変数の値を変更することです。

次にコード例を示します。

class Example extends Component{
   constructor(props){
      this.state = {temp:0};

      this.forceChange = this.forceChange.bind(this);
   }

   forceChange(){
      this.setState(prevState => ({
          temp: prevState.temp++
      })); 
   }

   render(){
      return(
         <div>{this.state.temp &&
             <div>
                  ... add code here ...
             </div>}
         </div>
      )
   }
}

強制的に再レン​​ダリングする必要がある場合は、this.forceChange()を呼び出します。


0

ES6-私に役立つ例を含めています。

「short ifステートメント」では、次のような空の関数を渡すことができます。

isReady ? ()=>{} : onClick

これは最短のアプローチのようです。

()=>{}


0

あなたが説明していることを達成するために、this.forceUpdate()を試してください。


このアクションは何をしますか?
ドミニク

0

もう一つの方法は、呼び出しているsetState状態を保持します:

this.setState(prevState=>({...prevState}));


0

forceUpdate()ですが、誰かがそれについて話すのを耳にしたことがあるたびに、フォローアップされてきたので、これを使用しないでください。


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