Usando a função connect () do react-redux

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 .

imagem

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:

  1. 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.
  2. 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:

  1. <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.
  2. 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 --save 

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]); // state console.log(arguments[1]); // ownProps }; 

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]); // state console.log(args[1]); // ownProps }; 

Declaração de função com um parâmetro


Considere o seguinte exemplo:

 const mapStateToProps = function(state) { console.log(state); // state console.log(arguments[1]); // undefined }; 

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); // state console.log(ownProps); // {} }; 

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); // state console.log(ownProps); // ownProps }; 

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?

Source: https://habr.com/ru/post/pt423157/


All Articles