web-dev-qa-db-fra.com

Afficher / masquer les composants ReactJS sans perdre leur état interne?

J'ai caché/montré des composants React en ne les rendant pas, par exemple:

render: function() {
  var partial;
  if (this.state.currentPage === 'home') {
    partial = <Home />;
  } else if (this.state.currentPage === 'bio') {
    partial = <Bio />;
  } else {
    partial = <h1>Not found</h1>
  }
  return (
    <div>
      <div>I am a menu that stays here</div>
      <a href="#/home">Home</a> <a href="#/bio">Bio</a>
      {partial}
    </div>
  );
}

mais dites simplement que le <Bio/> le composant a beaucoup d'état interne. Chaque fois que je recrée le composant, il perd son état interne et revient à son état d'origine.

Je sais bien sûr que je pourrais stocker les données quelque part et les transmettre via des accessoires ou simplement y accéder globalement, mais ces données n'ont pas vraiment besoin de vivre en dehors du composant. Je pourrais également masquer/afficher des composants à l'aide de CSS (display:none), mais je préfère les masquer/afficher comme ci-dessus.

Quelle est la meilleure pratique ici?

EDIT: Peut-être une meilleure façon d'énoncer le problème est d'utiliser un exemple:

Ignorez React et supposez que vous utilisiez simplement une application de bureau qui avait une boîte de dialogue de configuration avec un composant Tab appelé A, qui a 2 onglets, nommés 1 et 2.

Supposons que l'onglet A.1 possède un champ de texte e-mail et que vous remplissiez votre adresse e-mail. Ensuite, vous cliquez sur l'onglet A.2 pendant une seconde, puis cliquez sur l'onglet A.1. Que s'est il passé? Votre adresse e-mail ne serait plus là, elle aurait été réinitialisée à rien car l'état interne n'était stocké nulle part.

L'intériorisation de l'état fonctionne comme suggéré dans l'une des réponses ci-dessous, mais uniquement pour le composant et ses enfants immédiats. Si vous aviez des composants imbriqués de manière arbitraire dans d'autres composants, par exemple, Tabs in Tabs in Tabs, la seule façon pour eux de conserver leur état interne est de l'externaliser quelque part ou d'utiliser le display:none approche qui garde en fait tous les composants enfants à tout moment.

Il me semble simplement que ce type de données n'est pas des données dont vous voulez salir l'état de votre application ... ou même que vous devez même y penser. Il semble que les données que vous devriez pouvoir contrôler au niveau du composant parent, et choisir de conserver ou de supprimer, sans utiliser le display:none approchez-vous et sans vous préoccuper des détails sur la façon dont il est stocké.

24
Brad Parks

Une option serait de déplacer le conditionnel à l'intérieur du composant lui-même:

Bio = React.createClass({
    render: function() {
        if(this.props.show) {
            return <p>bio comp</p>
        } else {
            return null;
        }
    }
});

<Bio show={isBioPage} />

Que ce soit ou non une "meilleure pratique" dépend probablement de la situation exacte.

10
Colin Ramsay

Malheureusement, style={{display: 'none'}} astuce ne fonctionne que sur l'élément DOM normal, pas React composant. Je dois encapsuler le composant à l'intérieur d'un div. Je n'ai donc pas à cascader l'état au sous-composant.

<div className="content">
  <div className={this.state.curTab == 'securities' ? 'active' : ''}>
    <Securities />
  </div>
  <div className={this.state.curTab == 'plugins' ? 'active' : ''}>
    <Plugins />
  </div>
</div>
6
osamu

On dirait la documentation officielle suggère cacher les enfants avec état avec style={{display: 'none'}}

3
nilgun

Le problème fondamental ici est qu'en React vous n'êtes autorisé qu'à monter le composant sur son parent, ce qui n'est pas toujours le comportement souhaité. Mais comment résoudre ce problème?

Je propose la solution, adressée pour résoudre ce problème. Une définition plus détaillée du problème, src et des exemples peuvent être trouvés ici: https://github.com/fckt/react-layer-stack#rationale

Raisonnement

react/react-dom vient est livré avec 2 hypothèses/idées de base:

  • chaque interface utilisateur est naturellement hiérarchique. C'est pourquoi nous avons l'idée de components qui s'enveloppent
  • react-dom monte le composant enfant (physiquement) sur son nœud DOM parent par défaut

Le problème est que parfois la deuxième propriété n'est pas ce que vous voulez dans votre cas. Parfois, vous souhaitez monter votre composant dans un nœud DOM physique différent et maintenir en même temps une connexion logique entre le parent et l'enfant.

Un exemple canonique est un composant de type info-bulle: à un moment donné du processus de développement, vous pourriez constater que vous devez ajouter une description pour votre UI element: il sera rendu en calque fixe et devrait connaître ses coordonnées (qui sont que UI element coord ou coordonnées de la souris) et en même temps, il a besoin d'informations si elle doit être affichée maintenant ou non, son contenu et un certain contexte des composants parents. Cet exemple montre que parfois la hiérarchie logique ne correspond pas à la hiérarchie DOM physique.

Jetez un oeil à https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example pour voir l'exemple concret qui est la réponse à votre question (jetez un œil à la propriété "use"):

import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
  const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
  return (
    <Cell {...props}>
        // the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
        <Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
            hideMe, // alias for `hide(modalId)`
            index } // useful to know to set zIndex, for example
            , e) => // access to the arguments (click event data in this example)
          <Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
            <ConfirmationDialog
              title={ 'Delete' }
              message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
              confirmButton={ <Button type="primary">DELETE</Button> }
              onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
              close={ hideMe } />
          </Modal> }
        </Layer>

        // this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
        <LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
          <div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
            <Icon type="trash" />
          </div> }
        </LayerContext>
    </Cell>)
// ...
2
fckt