Un reductor es un función que determina cambios a una estado de la aplicación. Usa el acción recibe para determinar este cambio. Tenemos herramientas, como Redux, que ayudan a administrar los cambios de estado de una aplicación en una sola tienda para que se comporte de manera consistente.
¿Por qué mencionamos Redux cuando hablamos de reductores? Redux se basa en gran medida en funciones reductoras que toman el estado anterior y una acción para ejecutar el siguiente estado.
Nos vamos a centrar de lleno en los reductores en esta publicación. Nuestro objetivo es sentirnos cómodos trabajando con la función reductora para que podamos ver cómo se usa para actualizar el estado de una aplicación y, en última instancia, comprender el papel que desempeñan en un administrador de estado, como Redux.
Qué entendemos por “estado”
Los cambios de estado se basan en la interacción de un usuario o incluso en algo como una solicitud de red. Si el estado de la aplicación es administrado por Redux, los cambios ocurren dentro de una función reductora; este es el único lugar donde ocurren los cambios de estado. La función reductora hace uso de la estado inicial de la aplicación y algo llamado acción, para determinar cuál es el nuevo estado se vera como.
Si estuviéramos en clase de matemáticas, podríamos decir:
initial state + action = new state
En términos de una función reductora real, se ve así:
const contactReducer = (state = initialState, action) => {
// Do something
}
¿De dónde obtenemos ese estado y acción iniciales? Esas son las cosas que definimos.
El parámetro de estado
El state
El parámetro que se pasa a la función reductora debe ser el estado actual de la aplicación. En este caso, lo llamamos nuestro initialState
porque será el primer (y actual) estado y nada lo precederá.
contactReducer(initialState, action)
Digamos que el estado inicial de nuestra aplicación es una lista vacía de contactos y nuestra acción es agregar un nuevo contacto a la lista.
const initialState = {
contacts: []
}
Eso crea nuestro initialState
, que es igual al state
parámetro que necesitamos para la función reductora.
El parámetro de acción
Un action
es un objeto que contiene dos claves y sus valores. La actualización de estado que ocurre en el reductor siempre depende del valor de action.type
. En este escenario, estamos demostrando lo que sucede cuando el usuario intenta crear un nuevo contacto. Entonces, definamos el action.type
como NEW_CONTACT
.
const action = {
type: 'NEW_CONTACT',
name: 'John Doe',
location: 'Lagos Nigeria',
email: 'johndoe[email protected]'
}
Normalmente hay un payload
valor que contiene lo que el usuario está enviando y que se usaría para actualizar el estado de la aplicación. Es importante tener en cuenta que action.type
es obligatorio, pero action.payload
es opcional. Haciendo uso de payload
aporta un nivel de estructura a la apariencia del objeto de acción.
Estado de actualización
El estado está destinado a ser immutable
, lo que significa que no debe cambiarse directamente. Para crear un estado actualizado, podemos hacer uso de Object.assign
u opte por el operador de propagación.
Object.assign
const contactReducer = (state, action) => {
switch (action.type) {
case 'NEW_CONTACT':
return Object.assign({}, state, {
contacts: [
...state.contacts,
action.payload
]
})
default:
return state
}
}
En el ejemplo anterior, hicimos uso de la Object.assign()
para asegurarnos de que no cambiamos el valor del estado directamente. En cambio, nos permite devolver un nuevo objeto que se llena con el estado que se le pasa y la carga útil enviada por el usuario.
Para hacer uso de Object.assign()
, es importante que el primer argumento sea un objeto vacío. Pasar el estado como primer argumento provocará una mutación, que es lo que estamos tratando de evitar para mantener la coherencia.
El operador de propagación
La alternativa a object.assign()
es hacer uso del operador de propagación, así:
const contactReducer = (state, action) => {
switch (action.type) {
case 'NEW_CONTACT':
return {
...state, contacts:
[...state.contacts, action.payload]
}
default:
return state
}
}
Esto asegura que el estado entrante permanezca intacto mientras agregamos el nuevo elemento al final.
Trabajar con una declaración de cambio
Anteriormente, notamos que la actualización que ocurre depende del valor de action.type
. La declaración de cambio determina condicionalmente el tipo de actualización con la que estamos tratando, según el valor de la action.type
.
Eso significa que un reductor típico se verá así:
const addContact = (state, action) => {
switch (action.type) {
case 'NEW_CONTACT':
return {
...state, contacts:
[...state.contacts, action.payload]
}
case 'UPDATE_CONTACT':
return {
// Handle contact update
}
case 'DELETE_CONTACT':
return {
// Handle contact delete
}
case 'EMPTY_CONTACT_LIST':
return {
// Handle contact list
}
default:
return state
}
}
Es importante que volvamos a declarar nuestro default
para cuando el valor de action.type
especificado en el objeto de acción no coincide con lo que tenemos en el reductor; digamos, si por alguna razón desconocida, la acción se ve así:
const action = {
type: 'UPDATE_USER_AGE',
payload: {
age: 19
}
}
Como no tenemos este tipo de tipo de acción, queremos devolver lo que tenemos en el estado (el estado actual de la aplicación). Todo eso significa que no estamos seguros de lo que el usuario está tratando de lograr en este momento.
Poniendo todo junto
Aquí hay un ejemplo simple de cómo implementé la función reductora en React.
Ver la pluma
ejemplo de reductor por Kingsley Silas Chijioke (@kinsomicrote)
en CodePen.
Puede ver que no utilicé Redux, pero es muy similar a la forma en que Redux usa reductores para almacenar y actualizar los cambios de estado. La actualización del estado principal ocurre en la función reductora y el valor que devuelve establece el estado actualizado de la aplicación.
¿Quieres probarlo? Puede ampliar la función de reducción para permitir que el usuario actualice la edad de un contacto. ¡Me gustaría ver qué se te ocurre en la sección de comentarios!
Comprender el papel que juegan los reductores en Redux debería brindarle una mejor comprensión de lo que sucede debajo del capó. Si está interesado en leer más sobre el uso de reductores en Redux, vale la pena consultar la documentación oficial.