Redux: contêiner vs componente?

Criado em 19 set. 2015  ·  46Comentários  ·  Fonte: reduxjs/redux

Nos exemplos, há sempre uma pasta chamada "container" e outra chamada "componente". Qual é o pensamento por trás dessa separação?

Os "contêineres" são componentes inteligentes ou componentes de rota ou algo mais? Os componentes em "componentes" devem ser sempre burros?

docs question

Comentários muito úteis

Eu chamo components componentes React encapsulados que são movidos exclusivamente por adereços e não falam com Redux. O mesmo que “componentes burros”. Eles devem permanecer os mesmos, independentemente do seu roteador, biblioteca de busca de dados, etc.

Eu chamo containers componentes React que estão cientes do Redux, Roteador, etc. Eles são mais acoplados ao aplicativo. O mesmo que “componentes inteligentes”.

Todos 46 comentários

Quanto a mim, container é o manipulador da rota, que também puxa o estado do redux para essa rota. Então eu passo meu estado como suporte.

Por exemplo,

containers / 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;

e, por exemplo,

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

Lendo os links que você forneceu:

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

Tenho mais a sensação de que "containers" são na verdade o que no redux é chamado de "componentes inteligentes" ... e as rotas / páginas devem ter sua própria pasta.

Não sei por que "contêiner" e "manipuladores de rota" estão relacionados de alguma forma?

É uma prática muito comum agrupar os componentes do seu aplicativo por rota. Como @theaqua apontou, também é comum tornar cada manipulador / componente de rota um componente inteligente / contêiner. Se você estiver usando combineReducers, muitas vezes se verá dividindo estados, rotas e contêineres nas mesmas linhas. Conseqüentemente, eles geralmente ficam associados.

@ronag Não achei que você precisasse fazer contêineres e páginas. Por que você não pode colocar o manipulador de rotas e o conector redux em um estado? É muito prático. Mantenha simples.

No meu aplicativo, tenho apenas 3 rotas / páginas. Por outro lado, tenho muitos painéis / módulos independentes. Fazer um grande connect não é viável, preciso pelo menos dividi-lo em termos de painéis.

Se eu fizer todo o mapeamento de estado para adereços e todos os dados de busca em um único arquivo, seria impossível de manter. Especialmente se eu fizer a busca de todos os dados no mesmo local.

Se você tiver 3 rotas, precisará de 3 manipuladores de rota. Por exemplo, A.jsx , B.jsx e C.jsx em /containers .

Em cada contêiner você puxa parte (não toda!) Do estado redux e vinculações de ações. Então você passa isso para os componentes como no meu exemplo. É muito fácil de manter, porque eu (e minha equipe) sabemos - conectar-se ao redux e as ações estão sempre em containers , nunca será em components (que é pequeno e frequentemente sem estado como no meu exemplo )

Acho que entendo sua sugestão. No entanto, como eu disse, esses 3 arquivos se tornarão muito complicados se eu colocar todos os meus dados na busca. Em nossa causa seria basicamente "Login, App, Logout", onde App conteria quase tudo.

Talvez eu esteja muito inclinado a Relay onde cada componente tem um contêiner que descreve quais dados buscar e como devem ser.

Vou tentar sua sugestão e ver onde vamos parar.

Eu chamo components componentes React encapsulados que são movidos exclusivamente por adereços e não falam com Redux. O mesmo que “componentes burros”. Eles devem permanecer os mesmos, independentemente do seu roteador, biblioteca de busca de dados, etc.

Eu chamo containers componentes React que estão cientes do Redux, Roteador, etc. Eles são mais acoplados ao aplicativo. O mesmo que “componentes inteligentes”.

@gaearon : Perfeito. Isso esclarece os exemplos. Obrigada.

@gaearon Portanto, "componentes burros" são componentes sem estado, que podem ser escritos em uma sintaxe mais simples desde React 0.14 , por exemplo:

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

Estou correcto?

@soulmachine sim.

Não necessariamente. A sintaxe não é o ponto.

Componentes "burros", também conhecidos como componentes de "apresentação", são componentes que recebem todos os dados por adereços, não estão cientes do Redux e especificam apenas a aparência, mas não o comportamento.

@theaqua @gaearon Obrigado!

Embora o termo "contêiner" já seja bastante popular na nomenclatura react / redux, decidimos chamá-los de "conectores" no projeto em que estou trabalhando, para evitar qualquer confusão com layout / contêineres gráficos.

aliás, como chamá-los foi discutido anteriormente aqui rackt / react-redux # 170. Eu mantenho tudo dentro de uma pasta components e outras de apresentação um nível mais profundo, dentro de components/presentational , e acho que está tudo bem porque é improvável que sejam necessários para outras partes do aplicativo além de contêineres.

@gaearon então é um componente que dispatch considerado "burro" ou "inteligente"? É particularmente útil ter um componente folha ou intermediário para despachar uma ação, em vez de borbulhar o componente de volta ao topo do evento para fazer o despacho.

Sim, pode ser ocasionalmente útil, embora eu prefira connect() tais componentes para que o criador de ação seja injetado como um suporte. Realmente não importa como você o chama :-)

E quando você está construindo um módulo de componentes? Por exemplo, suponha que eu tenha um módulo de nó separado para nosso menu de navegação <NavMenu> Quero que as pessoas façam códigos como este:

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

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

então eu apenas o nomeio 'NavMenuContainer'? isso parece estranho para mim. Devo nomear o componente NavMenuComponent? ambos parecem estranhos para mim. Neste caso, o componente apenas liga para obter 1 campo do estado. É realmente tão ruim apenas colocar a chamada em linha para se conectar assim?

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

Curioso para ouvir sua opinião @gaearon sobre quando (se houver) está "ok" para apenas conectar a chamada em linha ...

Não importa como você o chama. Por que não chamá-lo de NavMenu ?

Não entendo a pergunta sobre inlining.
Ajudaria se você apresentasse duas abordagens que está comparando.

ok então por exemplo.
opção 1 (2 arquivos separados, 1 para contêiner, 1 para componente)

containers / NavMenu

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

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

opção 2 (1 único arquivo contendo 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>;
  }
});

o que quero dizer com inlining é apenas ter um único arquivo (em oposição a 2 arquivos) que é o contêiner e o componente, já que há apenas um pouco sobre o currentPath que é necessário do estado. É algo que sempre deve ser evitado ou é razoável fazê-lo em casos simples como este?

Claro, é razoável fazer isso em casos simples.

OK legal. quando você traçaria a linha e diria "ok, eu moveria isso para 2 arquivos separados"?

Quando o componente começa a misturar questões de dados (como recuperar / calcular dados, como despachar ações) com apresentação (como parece). Quando se torna difícil testar ou reutilizar em outro contexto.

@benmonro Mais um pensamento. Se você estiver construindo um módulo de componentes, às vezes você deseja conectar esses componentes a diferentes partes da árvore de estado do seu aplicativo ou testar sua apresentação separadamente com vários estados. Nesse caso, ter connect dentro deste módulo de componentes limitaria essa capacidade.

obrigado @gaearon

@sompylasar ponto muito bom! obrigado

hmm ok depois de passar pelo meu código para refatorá-lo para dividi-lo, agora tenho uma pergunta de acompanhamento. suponha que eu também tenha um contêiner <NavMenuItem> . O componente <NavMenu> precisa fazer referência a <NavMenuItem /> como um elemento filho. Devo apenas fazer import NavMenuItem from '../containers/NavMenuItem' no componente NavMenu? Como isso afeta a testabilidade @sompylasar?

Eu faria do NavMenuItem um componente de apresentação puro, com todos os dados necessários passados ​​para ele por meio de adereços do NavMenu. Isso permitiria testá-lo separadamente. Assim, você teria dois componentes de apresentação (NavMenu, NavMenuItem) e um conectado (conectar (...) (NavMenuItem)).

Algo que estou perdendo nesta discussão: quando você os tem em um arquivo, não pode usar a renderização superficial para teste. Já vi pessoas exporem o componente de apresentação e o componente de contêiner para contornar isso e os testar separadamente; portanto, neste caso, prefiro ter dois arquivos para deixar explícito que são duas coisas. Isso também destaca a separação de preocupações aqui e o fato de que o componente de apresentação é independente e reutilizável.

FWIW Eu atualizei o artigo Componentes de apresentação e contêiner para refletir meu pensamento atual.

Não desencorajamos mais a criação de componentes de contêiner nos documentos atualizados.
http://redux.js.org/docs/basics/UsageWithReact.html

@gaearon em seu exemplo real no documento Redux parece que os componentes podem ter contêineres como filho.
Estou errado? Como isso pode afetar a testabilidade de um componente burro que renderiza dentro dele um componente inteligente?
No entanto, não encontro uma maneira de permitir que os componentes sejam os mais recentes na hierarquia ...

Eu tenho um aplicativo que processa um componente Simple Data List (Dumb).
Dentro dele, cada item deve estar conectado à loja, por isso é um item inteligente.

Tudo bem de acordo com o doc, mas pode trazer algum problema?

Agradecer!

Como isso pode afetar a testabilidade de um componente burro que renderiza dentro dele um componente inteligente?

Isso torna o teste um pouco mais difícil de configurar (você também precisa inicializar o armazenamento). Quando isso for um inconveniente, extraia mais componentes de apresentação que aceitem children para que você possa passar os componentes do contêiner para dentro. Em geral, a divisão é com você e você precisa avaliar as compensações (facilidade de escrever, refatorar, testar, etc.) e escolher como separar os componentes para você.

Ok, então não há maneira certa. Temos que avaliar caso a caso. Muitos
obrigado!!

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

Como isso pode afetar a testabilidade de um componente burro que renderiza dentro dele
um inteligente?

Isso torna o teste um pouco mais difícil de configurar (você precisa inicializar
a loja também). Quando isso for um inconveniente, extraia mais
componentes de apresentação que aceitam crianças para que você possa passar o contêiner
componentes dentro. Em geral, a divisão é com você, e você precisa
avaliar as compensações (facilidade de escrita, refatoração, teste, etc) e
escolha como separar os componentes para você.

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/rackt/redux/issues/756#issuecomment -181143304.

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

E os 'componentes de layout'? Quero dizer, quando você tem muitos contêineres que precisam estar em um único componente para passá-lo para o roteador / navegador, esse componente de embalagem será um 'contêiner de apresentação idiota', certo?

@ Emilios1995 Tenho o mesmo problema ...
Eu tenho um componente de página que uso dentro de um componente de layout.
Este componente de Layout possui menus, cabeçalhos, rodapé. O conteúdo é o filho passado de Página para Layout.
Menus e cabeçalhos são contêineres !! Portanto, o Layout é potencialmente um contêiner, mas não está conectado à loja.

Porém, se tento passar ao layout dos menus e do cabeçalho, tenho uma página (container) que renderiza o layout (componente) e passando para ela menus e cabeçalho (containers).

Fazendo isso a hierarquia está correta, mas tenho que repetir menus e cabeçalhos em todas as páginas, e dois deles são os mesmos em todas as páginas para mim.

@LucaColonnello Não entendo muito bem o problema sem o código. Posso pedir que você crie uma pergunta StackOverflow com um exemplo simples ilustrando seu problema? Eu ficaria feliz em dar uma olhada.

o mais cedo possível

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

@LucaColonnello https://github.com/LucaColonnello Eu não entendo
entender o problema sem o código. Posso pedir-lhe para criar um
Pergunta StackOverflow com um exemplo simples ilustrando seu problema? Identidade
fique feliz em dar uma olhada.

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/reactjs/redux/issues/756#issuecomment -189672067.

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

Acho que o artigo de Dan sobre o meio
https://medium.com/@dan_abramov/smart -and-dumb-components-7ca2f9a7c7d0 # .3y00gw1mq
esclarece todas as questões ...

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

@gaearon https://github.com/gaearon Não é um problema, acho que é
antes uma pergunta que fiz aqui:
http://stackoverflow.com/questions/35729025/should-the-route-handlers-use-containers-or-presentational-components?noredirect=1#comment59133192_35729025

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/reactjs/redux/issues/756#issuecomment -190820426.

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

@gaearon Eu me pergunto se um componente de apresentação pode conter componentes de contêiner, como pode ser reutilizável?
PARA SUA INFORMAÇÃO:

Eu me pergunto se um componente de apresentação pode conter componentes de contêiner, como pode ser reutilizável?

Se todos os seus cenários de uso incluem um contêiner específico dentro, não vejo o que não pode ser reutilizado nele. E se não, faça com que ele aceite this.props.children e crie outros componentes de apresentação que passem contêineres específicos ou componentes de apresentação para dentro.

@gaearon O [componente raiz] é um componente de contêiner no 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>
    );
  }

Obrigado.

@gaearon acima, você disse que prefere conectar componentes para obter acesso ao despacho (em vez de repassá-lo dos componentes pai).

Se você conectar esses componentes, e eles também tiverem props que _podem_ ser mapeados a partir dos redutores que estão sendo passados ​​pelo pai, você iria refatorá-los para vir de connect ? Ou use ownProps para mantê-los ignorados pelos pais.

Existe uma diferença funcional / de desempenho entre as duas opções?

Não tenho muita experiência em trabalhar com grandes projetos redux, mas tenho pesquisado e pensado muito sobre como otimizar estruturas de arquivos react / redux. Deixe-me saber se isso faz algum sentido ou não:

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)

outro conceito:

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 é apenas melhor manter componentes, contêineres e módulos redux separados (mesmo que eles compartilhem o mesmo nome)? Obrigado por qualquer contribuição.

Tenho trabalhado com projetos react-redux de nível empresarial e através da minha experiência, posso dizer uma coisa que depende unicamente de você como definir a arquitetura do seu projeto. Eu não sigo nenhuma das variações arquitetônicas baseadas em contêiner / componente porque elas são impraticáveis ​​no sentido de que o propósito do React é fazer a interface do usuário baseada em componentes reutilizáveis.

Então, eu vim com uma abordagem simples de agrupar projetos inteiros com base em Módulos e tem funcionado muito bem até agora em termos de escalabilidade, manutenção e legibilidade de código.

image
image
image

Para mim, contêiner e componente inteligente são exatamente os mesmos. A definição simples é:
Um contêiner / componente inteligente é aquele que contém marcação JSX + manipuladores de eventos + chamadas de api + conexão / MSTP / MSDP do redux.
Um componente burro é PURAMENTE um componente funcional e de apresentação.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

elado picture elado  ·  3Comentários

vraa picture vraa  ·  3Comentários

ilearnio picture ilearnio  ·  3Comentários

timdorr picture timdorr  ·  3Comentários

CellOcean picture CellOcean  ·  3Comentários