Redux: contenedor vs componente?

Creado en 19 sept. 2015  ·  46Comentarios  ·  Fuente: reduxjs/redux

En los ejemplos siempre hay una carpeta llamada "contenedor" y otra llamada "componente". ¿Cuál es el pensamiento detrás de esta separación?

¿Son los "contenedores" un componente inteligente o son componentes de ruta o algo más? ¿Deberían ser siempre tontos los componentes de "componentes"?

docs question

Comentario más útil

Llamo components componentes de React encapsulados que son impulsados ​​únicamente por accesorios y no hablan con Redux. Igual que los "componentes tontos". Deben permanecer iguales independientemente de su enrutador, biblioteca de obtención de datos, etc.

Llamo componentes containers React que son conscientes de Redux, Router, etc. Están más acoplados a la aplicación. Igual que los "componentes inteligentes".

Todos 46 comentarios

En cuanto a mí, container es el controlador de ruta, que también extrae el estado de redux para esa ruta. Luego paso mi estado como apoyo.

Por ejemplo,

contenedores / propiedades.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;

y, por ejemplo,

componentes / propiedades / 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;

Leyendo de los enlaces que proporcionó:

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

Tengo más la sensación de que los "contenedores" son en realidad lo que en redux se llama "componentes inteligentes" ... y las rutas / páginas deberían tener su propia carpeta.

No estoy seguro de por qué "contenedor" y "controladores de ruta" están relacionados de alguna manera.

Es una práctica muy común agrupar los componentes de su aplicación por ruta. Como señaló @theaqua , también es común hacer que cada controlador / componente de ruta sea un componente inteligente / contenedor. Si está utilizando combineReducers, a menudo se encontrará dividiendo el estado, las rutas y los contenedores en la misma línea. Por lo tanto, a menudo se asocian.

@ronag No pensé que necesitaras hacer contenedores y páginas. ¿Por qué no puede poner el controlador de ruta y el conector redux al estado en 1 lugar? Es muy útil. Mantenlo simple.

En mi aplicación solo tengo 3 rutas / páginas. Por otro lado, tengo muchos paneles / módulos independientes. Hacer un connect enorme no es factible. Necesito al menos dividirlo en términos de paneles.

Si hago todo el mapeo desde el estado hasta los accesorios y todos los datos recuperados en un solo archivo, sería imposible de mantener. Especialmente si busco todos los datos en la misma ubicación.

Si tiene 3 rutas, entonces necesita 3 controladores de ruta. Por ejemplo, A.jsx , B.jsx y C.jsx en /containers .

En cada contenedor, extraes parte (¡no la totalidad!) Del estado redux y las acciones se unen. Luego, pasa esto a componentes como en mi ejemplo. Es muy fácil de mantener, porque yo (y mi equipo) sabemos: me conecto a redux y las acciones se vinculan siempre en containers , nunca estará en components (que es pequeño y, a menudo, sin estado como en mi ejemplo ).

Creo que entiendo tu sugerencia. Sin embargo, como dije, esos 3 archivos se volverán muy complicados si coloco todos los datos que busco allí. En nuestra causa, básicamente sería "Iniciar sesión, Aplicación, Cerrar sesión", donde la Aplicación contendría casi todo.

Tal vez estoy demasiado predispuesto a Relay donde cada componente tiene un contenedor que describe qué datos buscar y cómo debería verse.

Probaré tu sugerencia y veré dónde terminamos.

Llamo components componentes de React encapsulados que son impulsados ​​únicamente por accesorios y no hablan con Redux. Igual que los "componentes tontos". Deben permanecer iguales independientemente de su enrutador, biblioteca de obtención de datos, etc.

Llamo componentes containers React que son conscientes de Redux, Router, etc. Están más acoplados a la aplicación. Igual que los "componentes inteligentes".

@gaearon : Perfecto. Eso aclara los ejemplos. Gracias.

@gaearon Entonces, los "componentes tontos" son componentes sin estado, que se pueden escribir en una sintaxis más simple desde React 0.14 , por ejemplo:

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

¿Estoy en lo correcto?

@soulmachine sí.

No necesariamente. La sintaxis no es el punto.

Los componentes "tontos", también conocidos como componentes "de presentación", son componentes que reciben todos los datos por accesorios, no son conscientes de Redux y solo especifican la apariencia pero no el comportamiento.

@theaqua @gaearon ¡Gracias!

Aunque el término "contenedor" ya es bastante popular en la nomenclatura react / redux, decidimos llamarlos "conectores" en el proyecto en el que estoy trabajando, para evitar cualquier confusión con los contenedores de diseño / gráficos.

por cierto, cómo llamarlos se discutió previamente aquí rackt / react-redux # 170. Guardo todo dentro de una carpeta components y las de presentación un nivel más profundo, dentro de components/presentational , y creo que está bien porque es poco probable que las necesiten otras partes de la aplicación que no sean contenedores.

@gaearon, ¿ es un componente que dispatch considera "tonto" o "inteligente"? Es particularmente útil tener un componente intermedio o de hoja para enviar una acción en lugar de enviar el evento al componente superior para realizar el envío.

Sí, puede ser útil ocasionalmente, aunque prefiero connect() tales componentes, por lo que el creador de acciones se inyecta como accesorio. Realmente no importa cómo lo llames :-)

¿Qué pasa cuando construye un módulo de componentes? Por ejemplo, supongamos que tengo un módulo de nodo separado para nuestro menú de navegación <NavMenu> Quiero que la gente haga un código como este:

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

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

entonces, ¿lo llamo 'NavMenuContainer'? Eso me parece raro. ¿Debería nombrar el componente NavMenuComponent en su lugar? ambos me parecen raros. En este caso, el componente solo llama a connect para obtener 1 campo del estado. ¿Es realmente tan malo simplemente alinear la llamada para conectarse de esta manera?

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

Curioso por escuchar tus pensamientos @gaearon sobre cuándo (si es que lo hay) está "bien" simplemente en línea la llamada de conexión ...

No importa cómo lo llames. ¿Por qué no llamarlo NavMenu ?

No entiendo la pregunta sobre la inserción.
Sería útil si presentara dos enfoques que está comparando.

bien, por ejemplo.
opción 1 (2 archivos separados, 1 para contenedor, 1 para componente)

contenedores / NavMenu

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

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

opción 2 (1 archivo único que contiene ambos):
componentes / 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>;
  }
});

lo que quiero decir con inlining es tener un solo archivo (a diferencia de 2 archivos) que es tanto el contenedor como el componente, ya que solo hay un poco sobre la ruta actual que se necesita del estado. ¿Es esto algo que siempre debe evitarse o es razonable hacerlo en casos simples como este?

Claro, es razonable hacer esto en casos simples.

está bien. ¿Cuándo trazarías la línea y dirías "ok, movería esto a 2 archivos separados"?

Cuando el componente comienza a mezclar preocupaciones de datos (cómo recuperar / calcular datos, cómo enviar acciones) con la presentación (cómo se ve). Cuando se vuelve difícil probar o reutilizar en un contexto diferente.

@benmonro Un pensamiento más. Si está creando un módulo de componentes, a veces querrá conectar estos componentes a diferentes partes del árbol de estado de su aplicación, o probar su presentación por separado con varios estados. En este caso, tener connect dentro de este módulo de componentes limitaría esta capacidad.

gracias @gaearon

@sompylasar ¡ muy buen punto! Gracias

hmm ok después de revisar mi código para refactorizarlo y dividirlo, ahora tengo una pregunta de seguimiento. supongamos que también tengo un contenedor <NavMenuItem> . El componente <NavMenu> necesita hacer referencia a <NavMenuItem /> como un elemento secundario. ¿Debo simplemente hacer import NavMenuItem from '../containers/NavMenuItem' en el componente NavMenu? ¿Cómo afecta esto a la capacidad de prueba @sompylasar?

Haría de NavMenuItem un componente de presentación puro con todos los datos necesarios pasados ​​a través de los accesorios de NavMenu. Esto permitiría probarlo por separado. Por lo tanto, tendría dos componentes de presentación (NavMenu, NavMenuItem) y uno conectado (connect (...) (NavMenuItem)).

Algo que me falta en esta discusión: cuando los tiene en un archivo, no puede usar el renderizado superficial para las pruebas. He visto a personas exponer tanto el componente de presentación como el componente de contenedor para solucionar esto, y luego probarlo por separado, por lo que en este caso prefiero tener dos archivos para dejar explícito que se trata de dos cosas. Esto también destaca la separación de preocupaciones aquí y el hecho de que el componente de presentación es independiente y reutilizable.

FWIW Actualicé el artículo de Componentes de contenedor y presentación para reflejar mi pensamiento actual.

Ya no desaconsejamos la creación de componentes de contenedor en los documentos actualizados.
http://redux.js.org/docs/basics/UsageWithReact.html

@gaearon en su ejemplo real en el documento de Redux parece que los componentes pueden tener contenedores como elementos secundarios.
¿Me equivoco? ¿Cómo puede esto afectar la capacidad de prueba de un componente tonto que hace que en su interior sea inteligente?
Sin embargo, no encuentro la manera de permitir que los componentes sean los últimos en la jerarquía ...

Tengo una aplicación que representa un componente de lista de datos simple (tonto).
En su interior, cada artículo debe estar conectado a la tienda, por lo que es inteligente.

Está bien según el documento, pero ¿puede traer algún problema?

¡Agradecer!

¿Cómo puede esto afectar la capacidad de prueba de un componente tonto que hace que en su interior sea inteligente?

Esto hace que las pruebas sean un poco más difíciles de configurar (también debe inicializar la tienda). Cuando esto sea un inconveniente, extraiga más componentes de presentación que acepten children para que pueda pasar los componentes del contenedor al interior. En general, la división depende de usted y debe evaluar las compensaciones (facilidad de escritura, refactorización, pruebas, etc.) y elegir cómo separar los componentes por sí mismo.

Ok, entonces no hay una forma correcta. Tenemos que evaluar caso por caso. Muchos
¡¡Gracias!!

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

¿Cómo puede esto afectar la capacidad de prueba de un componente tonto que se procesa dentro de él?
uno inteligente?

Esto hace que las pruebas sean un poco más difíciles de configurar (debe inicializar
la tienda también). Cuando esto sea un inconveniente, extraiga más
componentes de presentación que aceptan niños para que pueda pasar el contenedor
componentes en el interior. En general, la división depende de usted y debe
evaluar las compensaciones (facilidad de escritura, refactorización, pruebas, etc.), y
elija cómo separar los componentes usted mismo.

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/rackt/redux/issues/756#issuecomment -181143304.

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

¿Qué pasa con los 'componentes de diseño'? Quiero decir, cuando tiene muchos contenedores que necesitan estar en un solo componente para pasarlo al enrutador / navegador, este componente de envoltura será un 'contenedor de presentación tonto', ¿verdad?

@ Emilios1995 Tengo el mismo problema ...
Tengo un componente de página que se usa dentro de un componente de diseño.
Este componente de diseño tiene menús, encabezados, pie de página ... El contenido es el elemento secundario que se pasa de la página al diseño ...
¡Los menús y los encabezados son contenedores! Entonces Layout es potencialmente un contenedor, pero no está conectado a la tienda.

Sin embargo, si trato de pasar al diseño de los menús y el encabezado, tengo una página (contenedor) que representa el diseño (componente) y le paso los menús y el encabezado (contenedores).

Al hacer esto, la jerarquía es correcta, pero tengo que repetir los menús y el encabezado en cada página, y para mí algunos de ellos son los mismos en todas las páginas.

@LucaColonnello No entiendo muy bien el problema sin el código. ¿Puedo pedirle que cree una pregunta de StackOverflow con un ejemplo simple que ilustre su problema? Estaría feliz de echar un vistazo.

Lo antes posible

Il sabato 27 de febrero de 2016, Dan Abramov [email protected] ha.
scritto:

@LucaColonnello https://github.com/LucaColonnello No del todo
comprender el problema sin el código. ¿Puedo pedirle que cree un
¿Pregunta de StackOverflow con un ejemplo simple que ilustra su problema? Identificación
estar feliz de echar un vistazo.

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/reactjs/redux/issues/756#issuecomment -189672067.

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

Creo que el artículo de Dan sobre el medio
https://medium.com/@dan_abramov/smart -and-dumb-components-7ca2f9a7c7d0 # .3y00gw1mq
aclara todas las cuestiones ...

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

@gaearon https://github.com/gaearon No es un problema, creo que es
más bien una pregunta que he hecho aquí:
http://stackoverflow.com/questions/35729025/should-the-route-handlers-use-containers-or-presentational-components?noredirect=1#comment59133192_35729025

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/reactjs/redux/issues/756#issuecomment -190820426.

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

@gaearon Me pregunto si un componente de presentación puede contener componentes de contenedor en su interior, ¿cómo puede ser reutilizable?
FYI:

Me pregunto si un componente de presentación puede contener componentes de contenedor en su interior, ¿cómo puede ser reutilizable?

Si todos sus escenarios de uso incluyen un contenedor específico en su interior, no veo qué tiene de no reutilizable. Y si no, haga que acepte this.props.children y cree otros componentes de presentación que pasen contenedores específicos o componentes de presentación al interior.

@gaearon ¿Es el componente contenedor [componente raíz] en 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>
    );
  }

Gracias.

@gaearon arriba, dijo que prefiere conectar componentes para obtener acceso al envío (en lugar de pasarlo desde los componentes principales).

Si conecta estos componentes, y también tienen accesorios que _podrían_ ser mapeados desde los reductores que actualmente están pasando por el padre, ¿los refactorizaría para que provengan de connect ? O use ownProps para que los padres los pasen.

¿Existe una diferencia funcional / de rendimiento entre las dos opciones?

No tengo mucha experiencia trabajando con grandes proyectos redux, pero he estado investigando y pensando mucho en optimizar las estructuras de archivos react / redux. Déjame saber si esto tiene algún sentido o no:

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)

otro concepto:

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 

¿O es mejor mantener los componentes, contenedores y módulos redux separados (aunque compartan el mismo nombre)? Gracias por cualquier aporte.

He trabajado con proyectos react-redux a nivel empresarial y, a través de mi experiencia, puedo decir una cosa que depende únicamente de usted cómo definir la arquitectura de su proyecto. No sigo ninguna de las variaciones arquitectónicas basadas en contenedores / componentes porque no son prácticas en el sentido de que el propósito de React es crear una interfaz de usuario basada en componentes reutilizables.

Así que se me ocurrió un enfoque simple para agrupar proyectos completos basados ​​en módulos y ha funcionado muy bien hasta ahora en términos de escalabilidad, mantenibilidad y legibilidad del código.

image
image
image

Para mí, el contenedor y el componente inteligente son exactamente lo mismo. La definición simple es:
Un contenedor / componente inteligente es el que contiene marcado JSX + controladores de eventos + llamadas api + conexión / MSTP / MSDP de redux.
Un componente tonto es PURAMENTE un componente funcional y de presentación.

¿Fue útil esta página
0 / 5 - 0 calificaciones