Usando la función connect () de react-redux

El artículo, que publicamos hoy, analizará cómo crear componentes de contenedor en aplicaciones React relacionadas con el estado de Redux. Este material se basa en la descripción del mecanismo de gestión de estado en React utilizando el paquete react-redux . Se supone que ya tiene una comprensión básica de la arquitectura y la API de las bibliotecas de las que hablaremos. Si este no es el caso, consulte la documentación de React y Redux .

imagen

Acerca de la gestión de estado en aplicaciones JavaScript


React proporciona al desarrollador dos mecanismos principales para transferir datos a los componentes. Estas son propiedades (accesorios) y estado. Las propiedades son de solo lectura y permiten que los componentes principales pasen atributos a los componentes secundarios. Un estado es una entidad local encapsulada dentro de un componente que puede cambiar en cualquier momento del ciclo de vida del componente.

Dado que el estado es un mecanismo extremadamente útil utilizado para crear potentes aplicaciones dinámicas de React, es necesario administrarlo adecuadamente. Actualmente hay varias bibliotecas que proporcionan una arquitectura bien estructurada para administrar el estado de las aplicaciones. Entre ellos están Flux , Redux , MobX .

Redux es una biblioteca diseñada para crear contenedores utilizados para almacenar el estado de la aplicación. Ofrece al desarrollador herramientas de gestión de estado comprensibles que se comportan de manera predecible. Esta biblioteca es adecuada para aplicaciones escritas en JavaScript puro, así como para proyectos en los que se desarrollaron algunos frameworks. Redux es de tamaño pequeño, pero le permite escribir aplicaciones confiables que funcionan en diferentes entornos.

Aquí se explica cómo crear repositorios de 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;    }    }) 

Paquete React-redux


El paquete react-redux proporciona enlaces React para el contenedor de estado Redux, por lo que es extremadamente fácil conectar la aplicación React al repositorio de Redux. Esto le permite separar los componentes de una aplicación React en función de su relación con el repositorio. A saber, estamos hablando de los siguientes tipos de componentes:

  1. Componentes de presentación. Solo son responsables de la apariencia de la aplicación y no conocen el estado de Redux. Reciben datos a través de propiedades y pueden llamar a devoluciones de llamada, que también se les pasan a través de propiedades.
  2. Componentes del contenedor. Son responsables del funcionamiento de los mecanismos internos de la aplicación e interactúan con el estado de Redux. A menudo se crean usando react-redux, pueden despachar acciones de Redux. Además, se suscriben a los cambios de estado.

Los detalles sobre este enfoque de la división de responsabilidad de los componentes se pueden encontrar aquí . En este artículo, hablaremos principalmente sobre los componentes del contenedor conectados al estado de Redux usando react-redux.

El paquete react-redux tiene una interfaz muy simple. En particular, lo más interesante de esta interfaz se reduce a lo siguiente:

  1. <Provider store> : le permite crear un contenedor para una aplicación React y hacer que el estado Redux esté disponible para todos los componentes del contenedor en su jerarquía.
  2. connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) : le permite crear componentes de orden superior. Esto es necesario para crear componentes de contenedor basados ​​en los componentes básicos de React.

Instale react-redux para usar este paquete en el proyecto de la siguiente manera:

 npm install react-redux --save 

Basado en el supuesto de que ya ha configurado el repositorio de Redux para su aplicación React, aquí hay un ejemplo de cómo conectar la aplicación al repositorio de 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); 

Ahora puede crear componentes de contenedor que estén conectados al repositorio de Redux. Esto se realiza dentro de la jerarquía de AppRootComponent utilizando la API connect() .

¿Cuándo usar connect ()?


▍Creando componentes de contenedores


Como ya se mencionó, la API react-redux connect() se usa para crear componentes de contenedor que están conectados al repositorio de Redux. El almacenamiento al que se está conectando se obtiene del ancestro superior del componente utilizando el mecanismo de contexto React. La función connect() no es necesaria si solo crea componentes de presentación.

Si, en el componente Reaccionar, necesita recibir datos del almacenamiento, o necesita despachar acciones, o necesita hacer ambas cosas, puede convertir un componente regular en un componente contenedor envolviéndolo en un componente de orden superior devuelto por connect() de reaccion-redux. Así es como se ve:

 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); 

▍ Elimina la necesidad de suscribirse manualmente al almacenamiento de Redux


Puede crear el componente contenedor usted mismo y firmar manualmente el componente en el repositorio de Redux utilizando el store.subscribe() . Sin embargo, usar la función connect() significa aplicar algunas mejoras de rendimiento y optimizaciones que es posible que no pueda usar al usar otros mecanismos.

En el siguiente ejemplo, intentamos crear manualmente un componente contenedor y conectarlo al repositorio de Redux suscribiéndonos a él. Aquí nos esforzamos por implementar la misma funcionalidad que se muestra en el ejemplo 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; 

La función connect() , además, le da al desarrollador una flexibilidad adicional, lo que le permite configurar los componentes del contenedor para recibir propiedades dinámicas basadas en las propiedades que se les pasaron originalmente. Esto resulta ser muy útil para obtener selecciones de un estado basado en propiedades o para vincular generadores de acción a una variable específica de propiedades.

Si su aplicación React usa múltiples repositorios de Redux, connect() facilita la especificación del repositorio específico al que se debe conectar el componente contenedor.

Anatomy connect ()


La función connect() proporcionada por el paquete react-redux puede tomar hasta cuatro argumentos, cada uno de los cuales es opcional. Después de llamar a la función connect() , se devuelve un componente de orden superior que puede usarse para ajustar cualquier componente React.

Como la función devuelve un componente de orden superior, debe llamarse nuevamente, pasando el componente React básico para convertirlo en un componente contenedor:

 const ContainerComponent = connect()(BaseComponent); 

Aquí está la firma de la función connect() :

 connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) 

Argument argumento mapStateToProps


El argumento mapStateToProps es una función que devuelve un objeto regular u otra función. Al pasar este argumento connect() suscribe el componente contenedor a las actualizaciones del repositorio de Redux. Esto significa que se mapStateToProps función mapStateToProps cada vez que cambie el estado del repositorio. Si no está interesado en monitorear actualizaciones de estado, pase connect() como el valor de este argumento a undefined o null .

La función mapStateToProps declara con dos parámetros, el segundo de los cuales es opcional. El primer parámetro es el estado actual del repositorio de Redux. El segundo parámetro, si se pasa, es un objeto de las propiedades pasadas al componente:

 const mapStateToProps = function(state) { return {   profile: state.user.profile,   loggedIn: state.auth.loggedIn } } export default connect(mapStateToProps)(ProfileComponent); 

Si se devuelve un objeto regular de mapStateToProps , el objeto stateProps devuelto stateProps combina con las propiedades del componente. Puede acceder a estas propiedades en el componente de la siguiente manera:

 function ProfileComponent(props) { return (   props.loggedIn     ? <Profile profile={props.profile} />     : <div>Please login to view profile.</div> ) } 

Si mapStateToProps devuelve una función, esta función se usa como mapStateToProps para cada instancia del componente. Esto puede ser útil para mejorar el rendimiento de renderizado y para memorizar.

Argument argumento mapDispatchToProps


El argumento mapDispatchToProps puede ser un objeto o una función que devuelve un objeto normal u otra función. Para ilustrar mejor mapDispatchToProps , necesitamos generadores de acción. Supongamos que tenemos los siguientes generadores:

 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' }); 

Ahora considere los diversos usos de mapDispatchToProps .

Implementación predeterminada predeterminada


Si no utiliza su propia implementación mapDispatchToProps , representada por un objeto o función, se utilizará una implementación estándar, que implementará el método de almacenamiento dispatch() como una propiedad del componente. Puede usar esta propiedad en un 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); 

Transferencia de objetos


Si se usa un objeto como argumento para mapDispatchToProps , cada función en el objeto se tomará como un generador de acción de Redux y se envolverá en una llamada al método de repositorio dispatch() , que permitirá que se llame directamente. El objeto resultante con generadores de acción, dispatchProps , se combinará con las propiedades del componente.

El siguiente ejemplo muestra un ejemplo de construcción del argumento mapDispatchToProps , que es un objeto con generadores de acción, así como cómo los generadores se pueden usar como propiedades del 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); 

Transferencia de funciones


Cuando se utiliza la función mapDispatchToProps como argumento mapDispatchToProps el programador debe encargarse de devolver el objeto dispatchProps , que une los generadores de acciones utilizando el método de almacenamiento dispatch() . Esta función acepta, como primer parámetro, el método del repositorio dispatch() . Al igual que con mapStateToProps , la función también puede aceptar el segundo parámetro opcional ownProps , que describe la asignación con las propiedades originales pasadas al componente.

Si esta función devuelve otra función, la función devuelta se usa como mapDispatchToProps , que puede ser útil para mejorar el rendimiento de la representación y la memorización.

La función auxiliar bindActionCreators() de Redux se puede usar dentro de esta función para vincular generadores de acción al método de repositorio dispatch() .

El siguiente ejemplo muestra el uso de la función mapDispatchToProps . También demuestra el trabajo con la función auxiliar bindActionCreators() , que se usa para unir generadores de acción para trabajar con comentarios sobre las props.actions del 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); 

▍Argumento mergeProps


Si el argumento mergeProps se pasa a connect() , entonces es una función que toma los siguientes tres parámetros:

  • stateProps es el objeto de propiedad devuelto por la llamada mapStateToProps() .
  • dispatchProps : un objeto de propiedad con generadores de acción de mapDispatchToProps() .
  • ownProps : las propiedades originales obtenidas por el componente.

Esta función devuelve un objeto simple con propiedades que se pasarán al componente envuelto. Esto es útil para mapear condicionalmente parte del estado de un repositorio de Redux o generadores de acción basados ​​en propiedades.

Si connect() no pasa esta función, entonces se usa su implementación estándar:

 const mergeProps = (stateProps, dispatchProps, ownProps) => { return Object.assign({}, ownProps, stateProps, dispatchProps) } 

▍ Argumento que representa un objeto con parámetros


Un objeto opcional, pasado a la función connect() como cuarto argumento, contiene parámetros diseñados para cambiar el comportamiento de esta función. Entonces, connect() es una implementación especial de la función connectAdvanced() , acepta la mayoría de los parámetros disponibles para connectAdvanced() , así como algunos parámetros adicionales.

Aquí está la página de documentación, después de leer la cual puede averiguar qué parámetros se pueden usar con connect() y cómo modifican el comportamiento de esta función.

Usando la función connect ()


▍ Crear almacenamiento


Antes de convertir un componente React normal en un componente contenedor utilizando connect() , debe crear un repositorio de Redux al que se conectará este componente.

Supongamos que tenemos un componente contenedor NewComment , que se utiliza para agregar nuevos comentarios a la publicación y, además, muestra un botón para enviar comentarios. El código que describe este componente puede verse así:

 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 se use en la aplicación, será necesario describir el repositorio de Redux al que se debe conectar este componente. De lo contrario, se producirá un error. Esto se puede hacer de dos maneras, que ahora consideraremos.

Establecer la propiedad de la tienda en un componente contenedor


La primera forma de equipar un componente con un repositorio de Redux es pasar un enlace a dicho repositorio como el valor de la propiedad de la store del componente:

 import React from 'react'; import store from './reduxStore'; import NewComment from './components/NewComment'; function CommentsApp(props) { return <NewComment store={store} /> } 

Establecer la propiedad de la tienda en el componente <Provider>


Si desea configurar el repositorio de Redux para la aplicación solo una vez, entonces le interesará el método que ahora consideraremos. Por lo general, es adecuado para aplicaciones que usan un solo repositorio de Redux.

El paquete react-redux proporciona al desarrollador el componente <Provider> , que puede usarse para envolver el componente raíz de la aplicación. Acepta la propiedad de la store . Se supone que es un enlace al repositorio de Redux, que se planea utilizar en la aplicación. La propiedad de la store se pasa, de acuerdo con la jerarquía de la aplicación, a los componentes del contenedor, utilizando el 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')) 

Organization Organización de acceso OpenProps


Como ya se mencionó, las funciones mapStateToProps y mapDispatchToProps mapStateToProps a connect() se pueden declarar con el segundo parámetro ownProps , que son las propiedades del componente.
Sin embargo, hay un problema. Si el número de parámetros requeridos de la función declarada es inferior a 2, ownProps no se transmitirá. Pero si una función se declara sin parámetros necesarios o con al menos 2 parámetros, se pasará ownProps .

Considere varias opciones para trabajar con ownProps .

Declaración de función sin parámetros


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

En esta situación, ownProps pasa ownProps , ya que la función se declara sin los parámetros necesarios. Como resultado, funcionará el siguiente código escrito usando la nueva sintaxis para los parámetros restantes de ES6:

 const mapStateToProps = function(...args) { console.log(args[0]); // state console.log(args[1]); // ownProps }; 

Declaración de función con un parámetro


Considere el siguiente ejemplo:

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

Solo hay un parámetro, state . Como resultado, los arguments[1] toman el valor undefined debido al hecho de que ownProps no se transmite.

Declaración de función con parámetro predeterminado


 const mapStateToProps = function(state, ownProps = {}) { console.log(state); // state console.log(ownProps); // {} }; 

Solo hay un parámetro requerido, state , ya que el segundo parámetro, ownProps , es opcional porque tiene un valor predeterminado. Como resultado, dado que solo hay un parámetro requerido, ownProps no se pasa y la asignación se realiza con el valor predeterminado que se le asignó, es decir, con un objeto vacío.

Declarando una función con dos parámetros


 const mapStateToProps = function(state, ownProps) { console.log(state); // state console.log(ownProps); // ownProps }; 

Todo está organizado de manera muy simple. Es decir, en tal situación, la transferencia de ownProps debido al hecho de que la función se declara con dos parámetros requeridos.

Resumen


Después de dominar este material, aprendió cuándo y cómo usar la API connect() proporcionada por el paquete react-redux y diseñada para crear componentes de contenedor conectados al estado Redux. Aquí hablamos en detalle sobre la estructura de la función connect() y cómo trabajar con ella, sin embargo, si desea obtener más información sobre este mecanismo, en particular, familiarizarse con sus casos de uso, eche un vistazo a esta sección de la documentación de react-redux.

Estimados lectores! ¿Usas react-redux en tus proyectos?

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


All Articles