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 .

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:
- 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.
- 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:
<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.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
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]);
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]);
Declaración de función con un parámetro
Considere el siguiente ejemplo:
const mapStateToProps = function(state) { console.log(state);
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);
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);
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?
