Les conteneurs react-redux connect() -ed peuvent-ils implémenter des méthodes lifecyle comme componentDidMount?

je suis tombé sur un motif répété dans mon site react-redux: Un composant affiche les données d'une api web, et il doit être rempli automatiquement au chargement, sans aucune interaction avec l'utilisateur.

je veux initier le fetch async à partir d'un composant container, mais pour autant que je puisse dire la seule façon de le faire est à partir d'un événement du cycle de vie dans un composant display. cela semble rendre impossible de mettre toute la logique dans le conteneur et n'utiliser stupide apatride fonctionnelle composants pour affichage.

cela signifie que je ne peux pas utiliser un composant fonctionnel apatride pour tout composant qui nécessite des données asynchrones. Cela ne semble pas juste.

il semble que la "bonne" façon de faire cela serait de lancer d'une façon ou d'une autre des appels asynchrones à partir du container. Ensuite, lorsque l'appel est revenu, l'état serait mis à jour et le conteneur obtiendrait le nouvel état et passerait à son tour ceux à sa composante apatride via mapStateToProps().

mise en asynchrone les appels à mapStateToProps et mapDispatchToProps (je veux dire en fait appeler la fonction async, par opposition à la rendre en tant que propriété) n'a pas de sens.

alors ce que j'ai fini par faire c'est mettre le ou les appels async dans un refreshData() fonction exposée par mapDispatchToProps(), puis l'appelant à partir de deux ou plusieurs des méthodes du cycle de vie React:componentDidMount and componentWillReceiveProps.

Existe-t-il un moyen propre de mettre à jour l'état de stockage de redux sans mettre les appels de méthode lifecycle dans chaque composant qui a besoin d'async les données?

devrais-je faire ces appels plus haut dans la hiérarchie des composants (réduisant ainsi la portée de cette question, puisque seuls les composants "de haut niveau" auraient besoin d'écouter les événements du cycle de vie)?

Modifier:

juste pour qu'il n'y ait pas de confusion ce que je veux dire par un composant de conteneur connect()ed, voici un exemple très simple:

import React from 'react';
import { connect } from 'react-redux';
import {action} from './actions.js';

import MyDumbComponent from './myDumbComponent.jsx';

function mapStateToProps(state)
{
  return { something: state.xxxreducer.something  };
}

function mapDispatchToProps(dispatch)
{
  return { 
       doAction: ()=>{dispatch(action())}
  };
}

const MyDumbComponentContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(MyDumbComponent);

// Uh... how can I hook into to componentDidMount()? This isn't 
// a normal React class.

export default MyDumbComponentContainer;
26
demandé sur stone 2016-07-20 23:33:05

6 réponses

Jamie Dixon a écrit un paquet pour faire ça!

https://github.com/JamieDixon/react-lifecycle-component

Utilisation devrait ressembler à ceci:

const mapDispatchToProps = {
    componentDidMount: getAllTehDatas
}

...

export default connectWithLifecycle(mapStateToProps, mapDispatchToProps)(WrappedComponent) 
7
répondu stone 2016-09-28 18:53:42

modifier Après la discussion dans les commentaires et en y réfléchissant davantage, cette réponse est plus exploratoire et peut servir de partie de la conversation. Mais je ne pense pas que c'est la bonne réponse.

origine de réponse

sur le site de Redux il y a un exemple cela montre que vous n'avez pas à faire à la fois mapStateToProps et mapDispatchToProps. Vous pouvez simplement l'effet de levier connect's remarquée pour les accessoires, et d'utiliser une classe et implémenter les méthodes lifecycle sur le composant dumb.

dans l'exemple, l'appel connect est même dans le même fichier et le composant muet n'est même pas exporté, donc pour l'utilisateur du composant il est identique.

je peux comprendre ne pas vouloir émettre d'appels asynchrones depuis le composant display. Je pense qu'il y a une distinction entre l'émission des appels asynchrones à partir de là et l'envoi d'une action qui, avec thunks, déplace l'émission des appels asynchrones vers les actions (encore plus découplé du code React).

à titre d'exemple, voici un composant splash screen où j'aimerais faire une action async (comme le préchargement de l'actif) lorsque le composant display Monte:

SplashContainer.js

import { connect } from 'react-redux'
import Splash from '../components/Splash'
import * as actions from '../actions'

const mapStateToProps = (state) => {
  return {
    // whatever you need here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onMount: () => dispatch(actions.splashMount())
  }
}

const SceneSplash = connect(
  mapStateToProps,
  mapDispatchToProps
)(Splash)

export default SceneSplash

Splash.js

import React from 'react'

class Splash extends React.Component {
  render() {
    return (
      <div className="scene splash">
      <span className="fa fa-gear fa-spin"></span>
      </div>
    )
  }

  componentDidMount() {
    const { onMount } = this.props
    onMount()
  }
}

export default Splash

Vous pouvez voir la l'expédition arrive au conteneur, et vous pouvez imaginer dans le actions.splashMount() appeler nous émettons une requête http async ou faire d'autres choses async via thunks ou promesses.

modifier pour clarifier

Permettez-moi d'essayer de défendre l'approche. J'ai relu la question et je ne suis pas sûr à 100% que j'aborde la principale chose qu'elle vise, mais supportez-moi. Si Je ne suis pas encore tout à fait sur la bonne voie, j'ai une approche modifiée en dessous qui pourrait être plus proche de la marque.

"il doit être rempli à la charge" - l'exemple ci-dessus accomplit cette

"je veux lancer le asynchrones extraction à partir d'un conteneur" - dans le exemple il n'est pas initié à partir du composant display ou du conteneur, mais à partir d'une action async

"cela semble rendre impossible de mettre toute la logique dans le conteneur" - je pense que vous pouvez encore mettre toute logique supplémentaire nécessaire dans le conteneur. Comme indiqué, le code de chargement de données n'est pas dans le composant d'affichage (ou le conteneur) mais dans le créateur d'action async.

" cela signifie que je ne peux pas utiliser un composant fonctionnel apatride pour tout composant qui nécessite des données asynchrones." - dans l'exemple ci-dessus, l'affichage est apatride et fonctionnelle. Le seul lien est la méthode lifecycle qui invoque un rappel. Il n'a pas besoin de savoir ou de se soucier de ce que fait ce rappel. Il ne s'agit pas d'un cas où le composant display essaie d'être le propriétaire de la collecte de données async - il s'agit simplement de laisser le code qui gère qui sait quand une chose particulière s'est produite.

Jusqu'à présent, j'essaie de justifier comment l'exemple donné répond aux exigences de la question. Cela dit, si ce que vous êtes après avoir un composant d'affichage qui ne contient absolument aucun code lié à la charge de données asynchrones, même par des callbacks indirects - c'est-à-dire que le seul lien qu'il a est de consommer ces données via les accessoires qu'il reçoit quand les données à distance descendent, alors je suggérerais quelque chose comme ceci:

SplashContainer.js

import { connect } from 'react-redux'
import Splash from '../components/Splash'
import * as actions from '../actions'

const mapStateToProps = (state) => {
  return {
    // whatever you need here
  }
}

const mapDispatchToProps = (dispatch) => {
  dispatch(actions.splashMount())
  return {
    // whatever else here may be needed
  }
}

const SceneSplash = connect(
  mapStateToProps,
  mapDispatchToProps
)(Splash)

export default SceneSplash

Splash.js

import React from 'react'

class Splash extends React.Component {
  // incorporate any this.props references here as desired
  render() {
    return (
      <div className="scene splash">
      <span className="fa fa-gear fa-spin"></span>
      </div>
    )
  }
}

export default Splash

en envoyant l'action dans mapDispatchToProps vous laissez le code de cette action résider entièrement dans le conteneur. En fait, vous commencez l'appel asynchrone dès que le conteneur est instancié, plutôt que d'attendre l'affichage connecté composante de spin up et de montage. Cependant, si vous ne pouvez pas lancer l'appel async jusqu'à ce que componentDidMount() pour le composant display démarre, je pense que vous êtes intrinsèquement lié à un code comme dans mon premier exemple.

Je n'ai pas testé cette seconde approche pour voir si react ou redux s'en plaindrait, mais ça devrait marcher. Vous avoir accès à la méthode de répartition et être en mesure de l'appeler sans problème.

pour être honnête, ce second exemple, tout en enlevant tout le code lié à l'action asynchrone du composant display, me semble un peu drôle puisque nous faisons des choses non-mapping-of-dispatch-to-props dans la fonction éponyme. Et les conteneurs n'ont pas vraiment de componentDidMount pour l'exécuter autrement. Donc je suis un peu bizarre avec ça et je me pencherais vers la première approche. Ce n'est pas propre dans le sens" feels right", mais il est dans le sens" simple 1-liner".

6
répondu jinglesthula 2016-12-22 15:15:04