web-dev-qa-db-fra.com

Pouvez-vous forcer un composant React à restituer sans appeler setState?

J'ai un objet observable externe (au composant) sur lequel je souhaite écouter les modifications. Lorsque l'objet est mis à jour, il émet des événements de modification, puis je souhaite rendre le composant à chaque nouvelle modification détectée.

Avec un React.render de niveau supérieur, cela a été possible, mais dans un composant, cela ne fonctionne pas (ce qui est logique car la méthode render ne retourne qu'un objet).

Voici un exemple de code:

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

Un clic interne sur le bouton appelle this.render(), mais ce n’est pas ce qui provoque le rendu (vous pouvez le voir en action car le texte créé par {Math.random()} ne change pas). Cependant, si j'appelle simplement this.setState() au lieu de this.render(), cela fonctionne bien.

Donc, je suppose que ma question est la suivante: Est-ce que les composants React ont besoin avoir l'état pour pouvoir rendre à nouveau? Existe-t-il un moyen de forcer le composant à mettre à jour à la demande sans changer l'état?

462
Philip Walton

Dans votre composant, vous pouvez appeler this.forceUpdate() pour forcer la restitution. 

Documentation: https://facebook.github.io/react/docs/component-api.html

566
Crob

forceUpdate devrait être évité, car il s'écarte de l'état d'esprit de React. La documentation de React cite un exemple d'utilisation de forceUpdate:

Par défaut, lorsque l'état ou les accessoires de votre composant changent, votre composant sera rendu de nouveau. Cependant, si ceux-ci changent implicitement (par exemple: les données profondes d'un objet changent sans changer l'objet lui-même) ou si votre méthode render () dépend d'autres données, vous pouvez indiquer à React qu'il doit réexécuter render () en appelant forceUpdate ().

Cependant, j'aimerais proposer l'idée que, même avec des objets profondément imbriqués, forceUpdate est inutile. En utilisant une source de données immuable, le suivi des modifications devient peu coûteux; un changement entraînera toujours un nouvel objet. Il suffit donc de vérifier si la référence à l'objet a changé. Vous pouvez utiliser la bibliothèque Immutable JS pour implémenter des objets de données immuables dans votre application.

Normalement, vous devriez essayer d'éviter toute utilisation de forceUpdate () et ne lire que depuis this.props et this.state dans render (). Cela rend votre composant "pur" et votre application beaucoup plus simple et plus efficace. https://facebook.github.io/react/docs/component-api.html#forceupdate

Changer la clé de l'élément que vous voulez restituer fonctionnera. Définissez l'accessoire clé sur votre élément via l'état, puis lorsque vous souhaitez mettre à jour l'état défini pour avoir une nouvelle clé. 

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

Puis un changement se produit et vous réinitialisez la clé

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

Je tiens à noter que cela remplacera l'élément sur lequel la clé est en train de changer. Cela peut être utile, par exemple, lorsqu'un champ de saisie de fichier que vous souhaitez réinitialiser après le téléchargement d'une image. 

Alors que la vraie réponse à la question du PO serait forceUpdate(), j'ai trouvé cette solution utile dans différentes situations. Je tiens également à noter que si vous utilisez forceUpdate, vous voudrez peut-être revoir votre code et voir s’il existe une autre façon de faire les choses.

NOTE 1-9-2019: 

Ce qui précède (changer la clé) remplacera complètement l'élément. Si vous vous trouvez en train de mettre à jour la clé pour effectuer des changements, vous avez probablement un problème ailleurs dans votre code. L'utilisation de Math.random() dans la clé recréera l'élément à chaque rendu. Je ne recommanderais PAS de mettre à jour la clé comme celle-ci, car react utilise la clé pour déterminer le meilleur moyen de rétablir le rendu.

249
pizza-r0b

En fait, forceUpdate() est la seule solution correcte car setState() pourrait pas déclencher un rendu si une logique supplémentaire est implémentée dans shouldComponentUpdate() ou si elle renvoie simplement false.

forceUpdate ()

Si vous appelez forceUpdate(), render() sera appelé sur le composant, en ignorant shouldComponentUpdate(). plus...

setState ()

setState() déclenchera toujours un rendu à moins que la logique de rendu conditionnel soit implémentée dans shouldComponentUpdate(). plus...


forceUpdate() peut être appelé depuis votre composant par this.forceUpdate()

50
Qwerty

Si vous souhaitez que deux composants de React communiquent, lesquels ne sont pas liés par une relation (parent-enfant), il est conseillé d'utiliser Flux ou des architectures similaires. 

Ce que vous voulez faire est d’écouter les changements de composante observable store, qui contient le modèle et son interface, et en enregistrant les données qui modifient le rendu en tant que state dans MyComponent. Lorsque le magasin envoie les nouvelles données, vous modifiez l'état de votre composant, ce qui déclenche automatiquement le rendu.

Normalement, vous devriez essayer d'éviter d'utiliser forceUpdate(). De la documentation:

Normalement, vous devriez essayer d'éviter toute utilisation de forceUpdate () et ne lire que depuis this.props et this.state dans render (). Cela rend votre application beaucoup plus simple et plus efficace

28
gcedo

J'ai évité forceUpdate en suivant

WRONG WAY : n'utilise pas d'index comme clé

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

CORRECT WAY : Utilise l'identifiant de données comme clé, cela peut être un guide, etc.

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

donc en faisant une telle amélioration de code, votre composant seraUNIQUEet restituera naturellement

24
vijay

Donc, je suppose que ma question est la suivante: les composants React doivent-ils avoir un état dans ordre de re-rendre? Existe-t-il un moyen de forcer le composant à mettre à jour demande sans changer d'état?

Les autres réponses ont essayé d’illustrer comment vous pourriez le faire, mais le fait est que vous ne devriez pas . Même la solution simpliste consistant à changer la clé passe à côté de l'essentiel. La puissance de React consiste à abandonner le contrôle de la gestion manuelle lorsque quelque chose doit être rendu, mais plutôt à vous préoccuper de la façon dont quelque chose doit être mappé sur les entrées. Puis fournir un flux d'intrants.

Si vous devez forcer manuellement le re-rendu, vous ne ferez certainement pas quelque chose de bien.

11
Kyle Baker

Aucun État ne craint de refaire le rendu

Je stockais souvent mes données dans l’état lorsque j’ai commencé à utiliser react en pensant que je devais le faire pour le rendu. C'était très faux. Il s'avère que vous pouvez même éviter de faire des choses compliquées comme Flux et Redux si vous ouvrez des magasins simples et immuables.

BaseView classe à partir de laquelle hériter et gérer les mises à jour du magasin

import React, { Component } from 'react';
import { View } from 'react-native';

export default class BaseView extends Component {

  constructor(props) {
    super(props)
    this.state = {}
  }

  onChange = () => {
    this.setState(this.state) // dumb easy: triggers render
  }

  componentWillMount = () => {
    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)
    })
  }

}

Comment utiliser

import React, { Component } from 'react'
import { View, Text } from 'react-native'
import BaseView from './BaseView'
import myStore from './myStore'

class MyView extends BaseView {
  contructor(props) {
    super(props)
    this.stores = [myStore]
  }

  render() {
    return (<View><Text onPress={() => {
      myStore.update({ message: 'hi' + Date.now() })
    }}>{myStore.get().message}</Text></View>)
  }
}

Exemple de classe simple Store

var ee = require('event-emitter')
export default class Store {
  constructor() {
    this._data = {}
    this._eventEmitter = ee({})
  }
  get() {
    return {...this._data} // immutable
  }
  update(newData) {
    this._data = {...this._data, ...newData}
    this._eventEmitter.emit('change')
  }
  on(ev, fn) {
    this._eventEmitter.on(ev, fn)
  }
  off(ev, fn) {
    this._eventEmitter.off(ev, fn)
  }
}

Instance de magasin en tant que singleton

import Store from './Store'

const myStore = new Store()
export default myStore

Je pense que cela supprime tous les cadres cruellement nécessaires aux petites applications. Utilisez redux pour les applications de taille moyenne à grande bien qu’elles soient associées à Immutable.js.

update Vous devriez vous intéresser au développement récent des "hooks" dans React. Cela semble vraiment propre et minimal. 

https://reactjs.org/docs/hooks-intro.html

8
Jason Sebring

Vous pouvez le faire de plusieurs manières:

1. Utilisez la méthode forceUpdate():

Certains problèmes peuvent survenir lors de l'utilisation de la méthode forceUpdate(). Par exemple, il ignore la méthode shouldComponentUpdate() et restitue la vue, que la fonction shouldComponentUpdate() renvoie false ou non. Pour cette raison, il faut éviter autant que possible d'utiliser forceUpdate ().

2. Passage de this.state à la méthode setState()

La ligne de code suivante résout le problème de l'exemple précédent:

this.setState(this.state);

En réalité, tout ce que vous faites est de remplacer l'état actuel par l'état actuel, ce qui déclenche un nouveau rendu. Ce n'est toujours pas nécessairement la meilleure façon de faire les choses, mais cela résout certains des problèmes que vous pourriez rencontrer en utilisant la méthode forceUpdate ().

3
kojow7

Nous pouvons utiliser this.forceUpdate () comme ci-dessous.

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

La partie Element 'Math.random' dans le DOM n'est mise à jour que si vous utilisez setState pour restituer le composant.

Toutes les réponses ici sont correctes complétant la question pour la compréhension .. comme nous savons re-rendre un composant sans l'aide de setState ({}) est en utilisant le forceUpdate ().

Le code ci-dessus fonctionne avec setState comme ci-dessous.

 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);
2
Dhana

Pour accomplir ce que vous décrivez, veuillez essayer ceci.forceUpdate ().

0
API-Andy

La méthode forceUpdate(); fonctionnera, mais il est conseillé d’utiliser setState();

0
Chiamaka Ikeanyi

J'ai trouvé le meilleur pour éviter forceUpdate (). Une façon de forcer le re-rendu consiste à ajouter une dépendance de render () à une variable externe temporaire et à modifier la valeur de cette variable selon les besoins. 

Voici un exemple de code:

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

Appelez this.forceChange () lorsque vous devez forcer le rendu de nouveau.

0
raksheetbhat

ES6 - J'inclus un exemple qui m'a été utile:

Dans une "instruction if courte", vous pouvez transmettre une fonction vide comme ceci:

isReady ? ()=>{} : onClick

Cela semble être l'approche la plus courte.

()=>{}
0
MrDoda

Juste une autre réponse pour sauvegarder la réponse acceptée :-)

React décourage l'utilisation de forceUpdate() car ils ont généralement une approche très "c'est la seule façon de le faire" en ce qui concerne la programmation fonctionnelle. Cela convient dans de nombreux cas, mais de nombreux développeurs de React ont un fond OO, et avec cette approche, il est parfaitement correct d'écouter un objet observable.

Et si vous le faites, vous savez probablement que vous DEVEZ effectuer un nouveau rendu lorsque l'observable "se déclenche", et en tant que tel, vous DEVRIEZ utiliser forceUpdate() et c'est en fait un avantage que shouldComponentUpdate() N'EST PAS impliqué ici. 

Des outils tels que MobX, qui adopte une approche OO, le font réellement sous la surface (en fait, MobX appelle directement render())

0
Plaul

Il existe plusieurs façons de rendre votre composant:

La solution la plus simple consiste à utiliser la méthode forceUpdate ():

this.forceUpdate()

Une autre solution consiste à créer une clé non utilisée dans l'état (nonUsedKey) et à appeler la fonction setState avec la mise à jour de cette nonUsedKey:

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

Ou réécrire tout l'état actuel:

this.setState(this.state);

Le changement des accessoires fournit également un composant rendu.

0
Jackkobec

forceUpdate (), mais chaque fois que j'ai entendu quelqu'un en parler, cela a été suivi, vous ne devriez jamais l'utiliser.

0
Ian

Vous pouvez utiliser forceUpdate () pour plus de détails ( forceUpdate () ).

0
Harshit Singhai