O artigo que estamos traduzindo hoje discutirá como criar componentes de contêiner em aplicativos React relacionados ao estado do Redux. Este material é baseado na descrição do mecanismo de gerenciamento de estado no React usando o
pacote react-redux . Supõe-se que você já tenha um entendimento básico da arquitetura e API das bibliotecas sobre as quais falaremos. Se não for esse o caso, consulte a documentação
React e
Redux .

Sobre gerenciamento de estado em aplicativos JavaScript
O React fornece ao desenvolvedor dois mecanismos principais para transferir dados para componentes. Estas são propriedades (adereços) e estado. As propriedades são somente leitura e permitem que os componentes pai transmitam atributos aos componentes filhos. Um estado é uma entidade local encapsulada dentro de um componente que pode mudar a qualquer momento no ciclo de vida do componente.
Como o estado é um mecanismo extremamente útil usado para criar poderosos aplicativos dinâmicos do React, é necessário gerenciá-lo adequadamente. Atualmente, existem várias bibliotecas que fornecem uma arquitetura bem estruturada para gerenciar a integridade do aplicativo. Entre eles estão o
Flux ,
Redux ,
MobX .
Redux é uma biblioteca projetada para criar contêineres usados para armazenar o estado do aplicativo. Ele oferece ao desenvolvedor ferramentas de gerenciamento de estado compreensíveis que se comportam de maneira previsível. Essa biblioteca é adequada para aplicativos escritos em JavaScript puro, bem como para projetos no desenvolvimento dos quais algumas estruturas foram usadas. Redux é pequeno, mas permite escrever aplicativos confiáveis que funcionam em diferentes ambientes.
Veja como criar repositórios Redux:
import { createStore } from 'redux'; const initialState = { auth: { loggedIn: false } } const store = createStore((state = initialState, action) => { switch (action.type) { case "LOG_IN": return { ...state, auth: { loggedIn: true } }; break; case "LOG_OUT": return { ...state, auth: { loggedIn: false } }; break; default: return state; break; } })
Pacote React-redux
O pacote react-redux fornece ligações React para o contêiner de estado Redux, tornando extremamente fácil conectar o aplicativo React ao repositório Redux. Isso permite que você separe os componentes de um aplicativo React com base no relacionamento deles com o repositório. Ou seja, estamos falando dos seguintes tipos de componentes:
- Componentes de apresentação. Eles são responsáveis apenas pela aparência do aplicativo e não têm conhecimento do estado do Redux. Eles recebem dados através de propriedades e podem chamar retornos de chamada, que também são transmitidos a eles através de propriedades.
- Componentes do contêiner. Eles são responsáveis pela operação dos mecanismos internos do aplicativo e interagem com o estado do Redux. Eles geralmente são criados usando o react-redux, eles podem despachar ações do Redux. Além disso, eles assinam alterações de estado.
Detalhes sobre essa abordagem da divisão de responsabilidade dos componentes podem ser encontrados
aqui . Neste artigo, falaremos principalmente sobre componentes de contêiner conectados ao estado Redux usando react-redux.
O pacote react-redux tem uma interface muito simples. Em particular, o mais interessante dessa interface se resume ao seguinte:
<Provider store>
- permite criar um wrapper para um aplicativo React e disponibilizar o estado Redux para todos os componentes do contêiner em sua hierarquia.connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
- permite criar componentes de ordem superior. Isso é necessário para criar componentes de contêiner com base nos componentes básicos do React.
Instale o react-redux para usar este pacote no projeto da seguinte maneira:
npm install react-redux
Com base na suposição de que você já configurou o repositório Redux para seu aplicativo React, aqui está um exemplo de conexão do aplicativo ao repositório Redux:
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import createStore from './createReduxStore'; const store = createStore(); const rootElement = document.getElementById('root'); ReactDOM.render(( <Provider store={store}> <AppRootComponent /> </Provider> ), rootElement);
Agora você pode criar componentes de contêiner que estão conectados ao repositório Redux. Isso é feito dentro da hierarquia do
AppRootComponent
usando a API
connect()
.
Quando usar o connect ()?
Criando componentes de contêiner
Como já mencionado, a API react-redux
connect()
é usada para criar componentes de contêiner que estão conectados ao repositório Redux. O armazenamento ao qual você está se conectando é obtido do ancestral mais alto do componente usando o mecanismo de contexto React. A função
connect()
não será necessária se você criar apenas componentes de apresentação.
Se você, no componente React, precisar receber dados do armazenamento, despachar ações ou executar as duas ações, poderá converter um componente regular em um componente de contêiner, envolvendo-o em um componente de ordem superior retornado por
connect()
de reagir-redux. Aqui está o que parece:
import React from 'react'; import { connect } from 'react-redux'; import Profile from './components/Profile'; function ProfileContainer(props) { return ( props.loggedIn ? <Profile profile={props.profile} /> : <div>Please login to view profile.</div> ) } const mapStateToProps = function(state) { return { profile: state.user.profile, loggedIn: state.auth.loggedIn } } export default connect(mapStateToProps)(ProfileContainer);
▍ Eliminando a necessidade de assinar manualmente o armazenamento Redux
Você pode criar o componente do contêiner e assinar manualmente o componente no repositório Redux usando o comando
store.subscribe()
. No entanto, usar a função
connect()
significa aplicar algumas melhorias e otimizações de desempenho que talvez você não consiga usar ao usar outros mecanismos.
No exemplo a seguir, tentamos criar manualmente um componente de contêiner e conectá-lo ao repositório Redux, assinando-o. Aqui nos esforçamos para implementar a mesma funcionalidade, como mostrado no exemplo anterior.
import React, { Component } from 'react'; import store from './reduxStore'; import Profile from './components/Profile'; class ProfileContainer extends Component { state = this.getCurrentStateFromStore() getCurrentStateFromStore() { return { profile: store.getState().user.profile, loggedIn: store.getState().auth.loggedIn } } updateStateFromStore = () => { const currentState = this.getCurrentStateFromStore(); if (this.state !== currentState) { this.setState(currentState); } } componentDidMount() { this.unsubscribeStore = store.subscribe(this.updateStateFromStore); } componentWillUnmount() { this.unsubscribeStore(); } render() { const { loggedIn, profile } = this.state; return ( loggedIn ? <Profile profile={profile} /> : <div>Please login to view profile.</div> ) } } export default ProfileContainer;
A função
connect()
, além disso, oferece ao desenvolvedor flexibilidade adicional, permitindo configurar componentes de contêiner para receber propriedades dinâmicas com base nas propriedades que foram originalmente passadas para eles. Isso acaba sendo muito útil para obter seleções de um estado com base em propriedades ou para vincular geradores de ação a uma variável específica das propriedades.
Se seu aplicativo React usar vários repositórios Redux, o
connect()
facilita a especificação do repositório específico ao qual o componente do contêiner deve ser conectado.
Anatomia connect ()
A função
connect()
fornecida pelo pacote react-redux pode levar até quatro argumentos, cada um dos quais é opcional. Após chamar a função
connect()
, é retornado um componente de ordem superior que pode ser usado para quebrar qualquer componente React.
Como a função retorna um componente de ordem superior, ela precisa ser chamada novamente, passando o componente React básico para convertê-lo em um componente de contêiner:
const ContainerComponent = connect()(BaseComponent)
Aqui está a assinatura da função
connect()
:
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
Argument argumento mapStateToProps
O argumento
mapStateToProps
é uma função que retorna um objeto regular ou outra função. Passar esse argumento
connect()
inscreve o componente do contêiner nas atualizações do repositório Redux. Isso significa que a função
mapStateToProps
será chamada sempre que o estado do repositório for alterado. Se você não estiver interessado em monitorar atualizações de status, passe
connect()
como o valor desse argumento para
undefined
ou
null
.
A função
mapStateToProps
declarada com dois parâmetros, o segundo dos quais é opcional. O primeiro parâmetro é o estado atual do repositório Redux. O segundo parâmetro, se passado, é um objeto das propriedades passadas para o componente:
const mapStateToProps = function(state) { return { profile: state.user.profile, loggedIn: state.auth.loggedIn } } export default connect(mapStateToProps)(ProfileComponent);
Se um objeto regular for retornado de
mapStateToProps
, o objeto
stateProps
retornado
stateProps
combinado com as propriedades do componente. Você pode acessar essas propriedades no componente da seguinte maneira:
function ProfileComponent(props) { return ( props.loggedIn ? <Profile profile={props.profile} /> : <div>Please login to view profile.</div> ) }
Se
mapStateToProps
retornar uma função, essa função será usada como
mapStateToProps
para cada instância do componente. Isso pode ser útil para melhorar o desempenho da renderização e memorizar.
Argument argumento mapDispatchToProps
O argumento
mapDispatchToProps
pode ser um objeto ou uma função que retorna um objeto regular ou outra função. Para ilustrar melhor o
mapDispatchToProps
, precisamos de geradores de ação. Suponha que tenhamos os seguintes geradores:
export const writeComment = (comment) => ({ comment, type: 'WRITE_COMMENT' }); export const updateComment = (id, comment) => ({ id, comment, type: 'UPDATE_COMMENT' }); export const deleteComment = (id) => ({ id, type: 'DELETE_COMMENT' });
Agora considere os vários usos do
mapDispatchToProps
.
Implementação padrão padrão
Se você não usar sua própria implementação
mapDispatchToProps
, representada por um objeto ou função, será usada uma implementação padrão, que implementará o método de repositório
dispatch()
como uma propriedade do componente. Você pode usar esta propriedade em um componente como este:
import React from 'react'; import { connect } from 'react-redux'; import { updateComment, deleteComment } from './actions'; function Comment(props) { const { id, content } = props.comment; // props.dispatch() const editComment = () => props.dispatch(updateComment(id, content)); const removeComment = () => props.dispatch(deleteComment(id)); return ( <div> <p>{ content }</p> <button type="button" onClick={editComment}>Edit Comment</button> <button type="button" onClick={removeComment}>Remove Comment</button> </div> ) } export default connect()(Comment);
Transferência de objeto
Se um objeto for usado como argumento para
mapDispatchToProps
, cada função no objeto será tomada como um gerador de ação Redux e envolvida em uma chamada de método de repositório
dispatch()
, que permitirá que ele seja chamado diretamente. O objeto resultante com geradores de ação,
dispatchProps
, será combinado com as propriedades do componente.
O exemplo a seguir mostra um exemplo de construção do argumento
mapDispatchToProps
, que é um objeto com geradores de ação, além de como os geradores podem ser usados como propriedades do componente React:
import React from 'react'; import { connect } from 'react-redux'; import { updateComment, deleteComment } from './actions'; function Comment(props) { const { id, content } = props.comment; // , , const editComment = () => props.updatePostComment(id, content); const removeComment = () => props.deletePostComment(id); return ( <div> <p>{ content }</p> <button type="button" onClick={editComment}>Edit Comment</button> <button type="button" onClick={removeComment}>Remove Comment</button> </div> ) } // const mapDispatchToProps = { updatePostComment: updateComment, deletePostComment: deleteComment } export default connect(null, mapDispatchToProps)(Comment);
Transferência de função
Ao usar a função
mapDispatchToProps
como argumento
mapDispatchToProps
o programador deve cuidar do retorno do objeto
dispatchProps
, que implementa a ligação dos geradores de ação usando o método de armazenamento
dispatch()
. Esta função aceita, como primeiro parâmetro, o método de repositório
dispatch()
. Assim como no
mapStateToProps
, a função também pode aceitar o segundo parâmetro opcional
ownProps
, que descreve o mapeamento com as propriedades originais passadas para o componente.
Se essa função retornar outra função, a função retornada será usada como
mapDispatchToProps
, que pode ser útil para melhorar o desempenho da renderização e a memorização.
A função auxiliar
bindActionCreators()
do Redux pode ser usada dentro desta função para vincular geradores de ação ao método de repositório
dispatch()
.
O exemplo a seguir mostra o uso, na função de
mapDispatchToProps
, de uma função. Também demonstra o trabalho com a função auxiliar
bindActionCreators()
, usada para ligar geradores de ação para trabalhar com comentários sobre os
props.actions
do componente React:
import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import * as commentActions from './actions'; function Comment(props) { const { id, content } = props.comment; const { updateComment, deleteComment } = props.actions; // props.actions const editComment = () => updateComment(id, content); const removeComment = () => deleteComment(id); return ( <div> <p>{ content }</p> <button type="button" onClick={editComment}>Edit Comment</button> <button type="button" onClick={removeComment}>Remove Comment</button> </div> ) } const mapDispatchToProps = (dispatch) => { return { actions: bindActionCreators(commentActions, dispatch) } } export default connect(null, mapDispatchToProps)(Comment);
MerArgument mergeProps
Se o argumento
mergeProps
for passado para
connect()
, é uma função que aceita os três parâmetros a seguir:
stateProps
é o objeto de propriedade retornado da chamada mapStateToProps()
.dispatchProps
- um objeto de propriedade com geradores de ação de mapDispatchToProps()
.ownProps
- As propriedades originais obtidas pelo componente.
Esta função retorna um objeto simples com propriedades que serão passadas para o componente agrupado. Isso é útil para mapear condicionalmente parte do estado de um repositório Redux ou geradores de ação baseados em propriedades.
Se
connect()
não passar nesta função, sua implementação padrão será usada:
const mergeProps = (stateProps, dispatchProps, ownProps) => { return Object.assign({}, ownProps, stateProps, dispatchProps) }
RepresentingArgumento representando um objeto com parâmetros
Um objeto opcional, passado para a função
connect()
como o quarto argumento, contém parâmetros projetados para alterar o comportamento dessa função. Portanto,
connect()
é uma implementação especial da função
connectAdvanced()
, que aceita a maioria dos parâmetros disponíveis para
connectAdvanced()
, além de alguns parâmetros adicionais.
Aqui está a página
da documentação, após a leitura, que você pode descobrir quais parâmetros podem ser usados com o
connect()
e como eles modificam o comportamento dessa função.
Usando a função connect ()
▍Criar armazenamento
Antes de converter um componente React regular em um componente de contêiner usando
connect()
, você precisa criar um repositório Redux ao qual esse componente será conectado.
Suponha que tenhamos um componente de contêiner
NewComment
, que é usado para adicionar novos comentários à publicação e, além disso, exibe um botão para enviar comentários. O código que descreve esse componente pode ser assim:
import React from 'react'; import { connect } from 'react-redux'; class NewComment extends React.Component { input = null writeComment = evt => { evt.preventDefault(); const comment = this.input.value; comment && this.props.dispatch({ type: 'WRITE_COMMENT', comment }); } render() { const { id, content } = this.props.comment; return ( <div> <input type="text" ref={e => this.input = e} placeholder="Write a comment" /> <button type="button" onClick={this.writeComment}>Submit Comment</button> </div> ) } } export default connect()(NewComment);
Para que este componente seja usado no aplicativo, será necessário descrever o repositório Redux ao qual esse componente deve estar conectado. Caso contrário, ocorrerá um erro. Isso pode ser feito de duas maneiras, que consideraremos agora.
Configurando a propriedade da loja em um componente de contêiner
A primeira maneira de equipar um componente com um repositório Redux é passar um link para um repositório como o valor da propriedade de
store
do componente:
import React from 'react'; import store from './reduxStore'; import NewComment from './components/NewComment'; function CommentsApp(props) { return <NewComment store={store} /> }
Definindo a propriedade da loja no componente <Provider>
Se você deseja configurar o repositório Redux para o aplicativo apenas uma vez, estará interessado no método que consideraremos agora. Geralmente é adequado para aplicativos que usam apenas um repositório Redux.
O pacote react-redux fornece ao desenvolvedor o componente
<Provider>
, que pode ser usado para agrupar o componente raiz do aplicativo. Aceita a propriedade da
store
. Supõe-se que seja um link para o repositório Redux, que está planejado para ser usado no aplicativo. A propriedade
store
é passada, de acordo com a hierarquia do aplicativo, para os componentes do contêiner, usando o mecanismo de contexto React:
import React from 'react'; import ReactDOM from 'react-dom'; import store from './reduxStore'; import { Provider } from 'react-redux'; import NewComment from './components/NewComment'; function CommentsApp(props) { return <NewComment /> } ReactDOM.render(( <Provider store={store}> <CommentsApp /> </Provider> ), document.getElementById('root'))
OrganizationOpenProps Access Organization
Como já mencionado, as funções
mapDispatchToProps
e
mapDispatchToProps
mapStateToProps
para
connect()
podem ser declaradas com o segundo parâmetro
ownProps
, que é as propriedades do componente.
No entanto, há um problema. Se o número de parâmetros necessários da função declarada for menor que 2, o
ownProps
não será transmitido. Mas se uma função for declarada sem parâmetros obrigatórios ou com pelo menos 2 parâmetros,
ownProps
será passado.
Considere várias opções para trabalhar com
ownProps
.
Declaração de função sem parâmetros
const mapStateToProps = function() { console.log(arguments[0]);
Nessa situação,
ownProps
passado, pois a função é declarada sem os parâmetros necessários. Como resultado, o seguinte código escrito usando a nova sintaxe para os demais parâmetros ES6 funcionará:
const mapStateToProps = function(...args) { console.log(args[0]);
Declaração de função com um parâmetro
Considere o seguinte exemplo:
const mapStateToProps = function(state) { console.log(state);
Existe apenas um parâmetro,
state
. Como resultado, os
arguments[1]
assumem o valor
undefined
devido ao fato de o
ownProps
não ser transmitido.
Declaração de função com parâmetro padrão
const mapStateToProps = function(state, ownProps = {}) { console.log(state);
Existe apenas um parâmetro necessário,
state
, pois o segundo parâmetro,
ownProps
, é opcional porque possui um valor padrão. Como resultado, como existe apenas um parâmetro necessário,
ownProps
não
ownProps
passado e o mapeamento é executado com o valor padrão que foi designado a ele, ou seja, com um objeto vazio.
Declarando uma função com dois parâmetros
const mapStateToProps = function(state, ownProps) { console.log(state);
Tudo é organizado de maneira muito simples. Ou seja, nessa situação, a transferência de
ownProps
devido ao fato de a função ser declarada com dois parâmetros necessários.
Sumário
Depois de dominar este material, você aprendeu sobre quando e como usar a API
connect()
fornecida pelo pacote react-redux e projetada para criar componentes de contêiner conectados ao estado Redux. Aqui conversamos detalhadamente sobre a estrutura da função
connect()
e como trabalhar com ela, no entanto, se você quiser saber mais sobre esse mecanismo, em particular, se familiarizar com seus casos de uso, dê uma olhada
nesta seção da documentação do react-redux.
Caros leitores! Você usa react-redux em seus projetos?
