Redux: conteneur vs composant?

Créé le 19 sept. 2015  ·  46Commentaires  ·  Source: reduxjs/redux

Dans les exemples, il y a toujours un dossier appelé «conteneur» et un autre appelé «composant». Quelle est la pensée derrière cette séparation?

Les «conteneurs» sont-ils des composants intelligents ou sont-ils des composants de routage ou autre chose? Les composants sous «composants» doivent-ils toujours être stupides?

docs question

Commentaire le plus utile

J'appelle des composants React encapsulés components qui sont pilotés uniquement par des accessoires et ne parlent pas à Redux. Identique aux «composants stupides». Ils doivent rester les mêmes quel que soit votre routeur, votre bibliothèque de récupération de données, etc.

J'appelle les composants containers React qui connaissent Redux, Router, etc. Ils sont plus couplés à l'application. Identique aux «composants intelligents».

Tous les 46 commentaires

Quant à moi, container est le gestionnaire de route, qui extrait également l'état de redux pour cette route. Ensuite, je passe mon état comme accessoire.

Par example,

conteneurs / properties.jsx

import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect }  from 'react-redux';

import * as actions from 'actions/properties';
import Filter from 'components/properties/filter';
import List from 'components/properties/list';
import Pagination from 'components/properties/pagination';

class PropertiesContainer extends Component {
  render() {
    return (
      <section>
        <Filter filter={this.props.properties.params.filter} />
        <List items={this.props.properties.items} isFetching={this.props.properties.isFetching} />
        <Pagination pagination={this.props.properties.params.pagination} />
      </section>
    );
  }
}

function mapState(state) {
  const { properties } = state;

  return { properties };
}

function mapDispatch(dispatch) {
  return {
    actions: bindActionCreators(actions, dispatch),
  };
}

const Connector = connect(mapState, mapDispatch)(PropertiesContainer);

export default Connector;

et, par exemple,

composants / propriétés / pagination.jsx

import React, { Component } from 'react';
import Pager from 'components/ui/pager';

class Pagination extends Component {
  render() {
    const { total, offset, limit } = this.props.pagination;
    const current = offset / limit;

    return (
      <Pager total={total} current={current} />
    )
  }
}

export default Pagination;

Lecture à partir des liens que vous avez fournis:

"A container does data fetching and then renders its corresponding sub-component. "

J'ai plus l'impression que les "conteneurs" sont en fait ce que l'on appelle dans redux "composants intelligents" ... et les routes / pages devraient avoir leur propre dossier.

Je ne sais pas pourquoi les "conteneurs" et les "gestionnaires de routes" sont liés de quelque manière que ce soit?

Il est très courant de regrouper les composants de votre application par itinéraire. Comme @theaqua l'a souligné, il est également courant de faire de chaque gestionnaire / composant d'itinéraire un composant intelligent / conteneur. Si vous utilisez combineReducers, vous vous retrouverez souvent à fractionner l'état, les itinéraires et les conteneurs le long des mêmes lignes. Par conséquent, ils s'associent souvent ensemble.

@ronag Je ne pensais pas que vous deviez créer des conteneurs et des pages. Pourquoi vous ne pouvez pas placer le gestionnaire d'itinéraire et le connecteur redux à un seul endroit? C'est très pratique. Rester simple.

Dans mon application, je n'ai que 3 routes / pages. D'un autre côté, j'ai beaucoup de panneaux / modules indépendants. Faire un énorme connect n'est pas faisable, je dois au moins le diviser en termes de panneaux.

Si je fais tout le mappage de l'état aux accessoires et toutes les données récupérées dans un seul fichier, ce ne serait pas maintenable. Surtout si je récupère toutes les données au même endroit.

Si vous avez 3 itinéraires, vous avez donc besoin de 3 gestionnaires d'itinéraire. Par exemple, A.jsx , B.jsx et C.jsx in /containers .

Dans chaque conteneur, vous tirez une partie (pas entière!) De l'état de redux et des liaisons d'actions. Ensuite, vous transmettez cela à des composants comme dans mon exemple. C'est très maintenable, car moi (et mon équipe) sais - je me connecte au redux et les actions se lient toujours dans containers , ce ne sera jamais dans components (qui est petit et souvent sans état comme dans mon exemple ).

Je pense comprendre votre suggestion. Cependant, comme je l'ai dit, ces 3 fichiers deviendront très compliqués si j'y place toutes mes données. Dans notre cause, ce serait essentiellement "Login, App, Logout" où App contiendrait presque tout.

Peut-être que je suis trop biaisé pour Relay où chaque composant a un conteneur qui décrit les données à récupérer et à quoi elles doivent ressembler.

Je vais essayer votre suggestion et voir où nous en sommes.

J'appelle des composants React encapsulés components qui sont pilotés uniquement par des accessoires et ne parlent pas à Redux. Identique aux «composants stupides». Ils doivent rester les mêmes quel que soit votre routeur, votre bibliothèque de récupération de données, etc.

J'appelle les composants containers React qui connaissent Redux, Router, etc. Ils sont plus couplés à l'application. Identique aux «composants intelligents».

@gaearon : Parfait. Cela clarifie les exemples. Merci.

@gaearon Les "composants stupides" sont donc des composants sans état, qui peuvent être écrits dans une syntaxe plus simple Depuis React 0.14 , par exemple:

var Aquarium = (props) => {
  var fish = getFish(props.species);
  return <Tank>{fish}</Tank>;
};

Ai-je raison?

@soulmachine ouais .

Pas nécessairement. La syntaxe n'est pas le point.

Les composants «stupides», également appelés composants «de présentation», sont des composants qui reçoivent toutes les données par des accessoires, ne sont pas conscients de Redux et spécifient uniquement l'apparence mais pas le comportement.

@theaqua @gaearon Merci!

Bien que le terme "conteneur" soit déjà assez populaire dans la nomenclature react / redux, nous avons décidé de les appeler "connecteurs" dans le projet sur lequel je travaille, pour éviter toute confusion avec les conteneurs layout / graphiques.

btw, comment les appeler a déjà été discuté ici rackt / react-redux # 170. Je garde tout dans un dossier components et ceux de présentation un niveau plus profond, dans components/presentational , et je pense que c'est bien car ils ne seront probablement pas nécessaires à des parties de l'application autres que les conteneurs.

@gaearon est-ce qu'un composant dispatch considéré comme "stupide" ou "intelligent"? Il est particulièrement utile d'avoir un composant feuille ou intermédiaire pour distribuer une action au lieu de faire remonter l'événement en haut du composant pour effectuer la distribution.

Oui, cela peut être parfois utile bien que je préfère connect() tels composants afin que le créateur d'action soit injecté comme accessoire. Peu importe comment vous l'appelez :-)

Qu'en est-il lorsque vous créez un module de composants? Par exemple, supposons que j'ai un module de nœud séparé pour notre menu de navigation <NavMenu> Je veux que les gens fassent du code comme celui-ci:

import {NavMenu} from 'my-redux-aware-components';

export function myPage(props) {
  return (<div><NavMenu routes={props.routes} /></div>);
}

alors est-ce que je l'appelle simplement «NavMenuContainer»? cela me semble bizarre. Dois-je nommer le composant NavMenuComponent à la place? les deux me semblent bizarres. Dans ce cas, le composant appelle uniquement connect pour obtenir 1 champ de l'état. Est-ce vraiment si grave de simplement intégrer l'appel pour se connecter comme ça?

export default const NavMenu = connect(state => ({currentPath:state.routing.path})(React.createClas({...}));

Curieux de savoir ce que vous pensez de

Peu importe comment vous l'appelez. Pourquoi ne pas l'appeler NavMenu ?

Je ne comprends pas la question sur l'inlining.
Cela vous aiderait si vous présentiez deux approches que vous comparez.

ok donc par exemple.
option 1 (2 fichiers séparés, 1 pour le conteneur, 1 pour le composant)

conteneurs / NavMenu

import {connect} from 'react-redux';
import NavMenu from '../components/NavMenu';

export default connect(state => ({currentPath:state.routing.path})(NavMenu);

option 2 (1 seul fichier contenant les deux):
composants / NavMenu

import {connect} from 'react-redux';

export default connect(state => ({currentPath:state.routing.path})(React.createClass({
  render() {
      return <div>the menu {this.props.currentPath} goes here</div>;
  }
});

ce que je veux dire par inlining, c'est simplement avoir un seul fichier (par opposition à 2 fichiers) qui est à la fois le conteneur et le composant car il n'y a qu'un petit peu sur le currentPath qui est nécessaire de l'état. Est-ce juste quelque chose qui devrait toujours être évité ou est-il raisonnable de le faire dans des cas simples comme celui-ci?

Bien sûr, il est raisonnable de le faire dans des cas simples.

OK cool. quand tireriez-vous la ligne et diriez-vous "ok je vais déplacer ceci dans 2 fichiers séparés"?

Lorsque le composant commence à mélanger les questions de données (comment récupérer / calculer les données, comment répartir les actions) avec la présentation (à quoi cela ressemble). Quand il devient difficile de tester ou de réutiliser dans un contexte différent.

@benmonro Encore une réflexion. Si vous créez un module de composants, vous souhaiterez parfois connecter ces composants à différentes parties de l'arborescence d'état de votre application, ou tester leur présentation séparément avec différents états. Dans ce cas, avoir connect intérieur de ce module de composants limiterait cette capacité.

merci @gaearon

@sompylasar très bon point! Merci

hmm ok après avoir parcouru mon code pour le refactoriser pour le séparer, j'ai maintenant une question de suivi. supposons que j'ai aussi un conteneur <NavMenuItem> . Le composant <NavMenu> doit référencer <NavMenuItem /> comme élément enfant. Dois-je simplement faire import NavMenuItem from '../containers/NavMenuItem' dans le composant NavMenu? Comment cela affecte-t-il la testabilité @sompylasar?

Je ferais du NavMenuItem un composant de présentation pur avec toutes les données nécessaires transmises via des accessoires de NavMenu. Cela permettrait de le tester séparément. Ainsi, vous auriez deux composants de présentation (NavMenu, NavMenuItem) et un composant connecté (connect (...) (NavMenuItem)).

Une chose qui me manque dans cette discussion: lorsque vous les avez dans un fichier, vous ne pouvez pas utiliser le rendu superficiel pour les tests. J'ai vu des gens exposer à la fois le composant de présentation et le composant de conteneur pour contourner ce problème, puis les tester séparément, donc dans ce cas, je préfère avoir deux fichiers pour expliquer clairement que ce sont deux choses. Cela met également en évidence la séparation des préoccupations ici et le fait que le composant de présentation est indépendant et réutilisable.

FWIW J'ai mis à jour l'article sur les composants de présentation et de conteneur pour refléter ma pensée actuelle.

Nous ne décourageons plus la création de composants de conteneur dans les documents mis à jour.
http://redux.js.org/docs/basics/UsageWithReact.html

@gaearon dans votre exemple réel dans le document Redux semble que les composants peuvent avoir des conteneurs comme enfants.
Ai-je tort? Comment cela peut-il affecter la testabilité d'un composant stupide qui en fait un composant intelligent?
Cependant, je ne trouve pas le moyen de laisser les composants être les derniers dans la hiérarchie ...

J'ai une application qui rend un composant Simple Data List (Dumb).
À l'intérieur, chaque article doit être connecté au magasin, il s'agit donc d'un article intelligent.

C'est ok selon la doc, mais cela peut-il poser un problème?

Remercier!

Comment cela peut-il affecter la testabilité d'un composant stupide qui en fait un composant intelligent?

Cela rend les tests un peu plus difficiles à configurer (vous devez également initialiser le magasin). Lorsque c'est un inconvénient, extrayez plus de composants de présentation qui acceptent children afin de pouvoir passer des composants de conteneur à l'intérieur. En général, la division dépend de vous et vous devez évaluer les compromis (facilité d'écriture, refactorisation, tests, etc.) et choisir comment séparer les composants pour vous-même.

Ok, donc il n'y a pas de bonne façon. Nous devons évaluer au cas par cas.
Merci!!

Il lunedì 8 febbraio 2016, Dan Abramov [email protected] ha
scritto:

Comment cela peut-il affecter la testabilité d'un composant muet qui est rendu à l'intérieur
un intelligent?

Cela rend les tests un peu plus difficiles à configurer (vous devez initialiser
le magasin aussi). Lorsque c'est un inconvénient, extrayez-en plus
composants de présentation qui acceptent les enfants afin que vous puissiez passer le conteneur
composants à l'intérieur. En général, la division dépend de vous et vous devez
évaluer les compromis (facilité d'écriture, refactorisation, tests, etc.), et
choisissez comment séparer les composants pour vous-même.

-
Répondez directement à cet e-mail ou affichez-le sur GitHub
https://github.com/rackt/redux/issues/756#issuecomment -181143304.

Luca Colonnello
+39 345 8948718
luca. [email protected]

Qu'en est-il des «composants de mise en page»? Je veux dire que lorsque vous avez de nombreux conteneurs qui doivent être dans un seul composant pour le transmettre au routeur / navigateur, ce composant d'emballage va être un «conteneur de présentation stupide de conteneurs», n'est-ce pas?

@ Emilios1995 J'ai le même problème ...
J'ai un composant de page qui utilise à l'intérieur d'un composant de mise en page.
Ce composant de mise en page comporte des menus, des en-têtes, des pieds de page. Le contenu est l’enfant passé de la page à la mise en page.
Les menus et les en-têtes sont des conteneurs !! Ainsi, Layout est potentiellement un conteneur, mais n'est pas connecté au magasin.

Cependant, si j'essaie de passer à la mise en page des menus et de l'en-tête, j'ai une page (conteneur) qui rend la mise en page (composant) et lui passe les menus et l'en-tête (conteneurs).

En faisant cela, la hiérarchie est correcte, mais je dois répéter les menus et l'en-tête dans chaque page, et ils sont les mêmes dans chaque page pour moi.

@LucaColonnello Je ne comprends pas très bien le problème sans le code. Puis-je vous demander de créer une question StackOverflow avec un exemple simple illustrant votre problème? Je serais heureux de jeter un coup d'œil.

dès que possible

Il sabato 27 febbraio 2016, Dan Abramov [email protected] ha
scritto:

@LucaColonnello https://github.com/LucaColonnello Je ne sais pas tout à fait
comprendre le problème sans le code. Puis-je vous demander de créer un
Question StackOverflow avec un exemple simple illustrant votre problème? Identifiant
soyez heureux de jeter un oeil.

-
Répondez directement à cet e-mail ou affichez-le sur GitHub
https://github.com/reactjs/redux/issues/756#issuecomment -189672067.

Luca Colonnello
+39 345 8948718
luca. [email protected]

Je pense que l'article de Dan sur le médium
https://medium.com/@dan_abramov/smart -and-dumb-components-7ca2f9a7c7d0 # .3y00gw1mq
clarifie tous les problèmes ...

01/03/2016 18:19 GMT + 01:00 Emilio Srougo [email protected] :

@gaearon https://github.com/gaearon Ce n'est pas un problème, je pense que c'est
plutôt une question que j'ai posée ici:
http://stackoverflow.com/questions/35729025/should-the-route-handlers-use-containers-or-presentational-components?noredirect=1#comment59133192_35729025

-
Répondez directement à cet e-mail ou affichez-le sur GitHub
https://github.com/reactjs/redux/issues/756#issuecomment -190820426.

Luca Colonnello
+39 345 8948718
luca. [email protected]

@gaearon Je me demande que si un composant de présentation peut contenir des composants de conteneur à l'intérieur, comment peut-il être réutilisable?
POUR VOTRE INFORMATION:

Je me demande que si un composant de présentation peut contenir des composants de conteneur à l'intérieur, comment peut-il être réutilisable?

Si tous ses scénarios d'utilisation incluent un conteneur spécifique à l'intérieur, je ne vois pas ce qui n'est pas réutilisable. Et sinon, faites-le accepter this.props.children et créez d'autres composants de présentation qui passent des conteneurs spécifiques ou des composants de présentation à l'intérieur.

@gaearon Le composant conteneur [composant racine] est-il sur React-Router?

  • route.js
<Route path="/" component={Root}>
      <IndexRoute component={Main} />
      <Route path="/account/signIn" component={SignIn} />
</Route>
  • root.js
export default class Root extends React.Component {
  render() {
    return (
      <div>
        <div id="container" className="container">
          {this.props.children}
        </div>
      </div>
    );
  }

Merci.

@gaearon ci-dessus, vous avez dit que vous préférez connecter des composants afin d'accéder à la répartition (plutôt que de la transmettre à partir des composants parents).

Si vous connectez ces composants, et qu'ils ont également des accessoires qui _pourraient_ être mappés à partir des réducteurs qui sont actuellement transmis par le parent, les refactoriseriez-vous pour qu'ils proviennent de connect ? Ou utilisez ownProps pour les garder transmis par le parent.

Y a-t-il une différence fonctionnelle / de performance entre les deux options?

Je n'ai pas beaucoup d'expérience de travail avec d'énormes projets redux, mais j'ai beaucoup recherché et réfléchi à l'optimisation des structures de fichiers react / redux. Faites-moi savoir si cela a du sens ou non:

src/
  components/
    header/ 
      navigation.js # nav menu list
      index.js # Header component
  modules/
    header/
      actions.js # header actions (sticky scroll, collapse mobile menu, etc...)
      reducer.js # header actions reducer (export to modules index)
      index.js # HeaderContainer (connect to Header component)
    index.js # combineReducers and export default configureStore (and middleware)

un autre concept:

src/
  components/
    navigation.js
    logo.js
    link.js
    list.js
    item.js
  modules/
    header/
      actions.js # header actions 
      wrapper.js # header class (smart) component - to wrap header with functionality (was previously classified as container)
      container.js # header functional (dumb) component - to contain universal components
      index.js # header actions reducer - to export into modules rootReducer 

Ou est-il simplement préférable de séparer les composants, les conteneurs et les modules redux (même s'ils partagent le même nom)? Merci pour toute contribution.

J'ai travaillé avec des projets react-redux au niveau de l'entreprise et grâce à mon expérience, je peux dire une chose que cela dépend uniquement de vous comment définir l'architecture de votre projet. Je ne suis aucune des variations architecturales basées sur les conteneurs / composants car elles ne sont pas pratiques dans le sens où le but de React est de créer une interface utilisateur basée sur des composants réutilisables.

J'ai donc proposé une approche simple de regroupement de projets entiers basés sur des modules et cela a très bien fonctionné jusqu'à présent en termes d'évolutivité, de maintenabilité et de lisibilité du code.

image
image
image

Pour moi, le conteneur et le composant intelligent sont exactement les mêmes. La définition simple est:
Un conteneur / composant intelligent est celui qui contient le balisage JSX + les gestionnaires d'événements + les appels api + connect / MSTP / MSDP de redux.
Un composant stupide est un composant purement présentationnel et fonctionnel.

Cette page vous a été utile?
0 / 5 - 0 notes