Fundamentos de Redux, Parte 5: Interfaz de Usuario y React
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
- Cómo funciona un almacén de Redux con una interfaz de usuario
- Cómo usar Redux con React
Introducción
En la Parte 4: Almacén, vimos cómo crear un almacén de Redux, despachar acciones y leer el estado actual. También examinamos cómo funciona un almacén internamente, cómo los potenciadores y middleware nos permiten personalizarlo con capacidades adicionales, y cómo añadir Redux DevTools para ver qué sucede en nuestra app al despachar acciones.
En esta sección, añadiremos una Interfaz de Usuario para nuestra app de tareas. Veremos cómo funciona Redux con una capa de interfaz en general y cubriremos específicamente cómo Redux funciona junto con React.
Ten en cuenta que esta página y todo el tutorial "Esenciales" enseñan cómo usar nuestra API moderna de hooks de React-Redux. La antigua API connect aún funciona, pero hoy queremos que todos los usuarios de Redux usen la API de hooks.
Además, las demás páginas de este tutorial muestran intencionadamente patrones antiguos de lógica de Redux que requieren más código que los patrones de "Redux moderno" con Redux Toolkit, que enseñamos como el enfoque correcto para construir apps con Redux actualmente, para explicar los principios y conceptos detrás de Redux.
Consulta el tutorial "Redux Essentials" para ver ejemplos completos de "cómo usar Redux correctamente" con Redux Toolkit y hooks de React-Redux para aplicaciones del mundo real.
Integración de Redux con una interfaz de usuario
Redux es una biblioteca independiente de JavaScript. Como ya hemos visto, puedes crear y usar un almacén de Redux incluso sin tener configurada una interfaz de usuario. Esto también significa que puedes usar Redux con cualquier framework de UI (o incluso sin ningún framework de UI), y usarlo tanto en cliente como en servidor. Puedes escribir apps de Redux con React, Vue, Angular, Ember, jQuery o JavaScript vanilla.
Dicho esto, Redux fue diseñado específicamente para funcionar bien con React. React te permite describir tu UI como una función de tu estado, y Redux contiene el estado y lo actualiza en respuesta a acciones.
Por ello, usaremos React en este tutorial mientras construimos nuestra app de tareas, y cubriremos los fundamentos de cómo usar React con Redux.
Antes de llegar a esa parte, echemos un vistazo rápido a cómo interactúa Redux con una capa de UI en general.
Integración básica de Redux e interfaz de usuario
Usar Redux con cualquier capa de UI requiere unos pasos consistentes:
-
Crear un almacén de Redux
-
Suscribirse a actualizaciones
-
Dentro de la callback de suscripción:
- Obtener el estado actual del almacén
- Extraer los datos necesarios para esta parte de la UI
- Actualizar la UI con los datos
-
Si es necesario, renderizar la UI con el estado inicial
-
Responder a entradas de UI despachando acciones de Redux
Volvamos al ejemplo de la app de contador que vimos en la Parte 1 y veamos cómo sigue esos pasos:
// 1) Create a new Redux store with the `createStore` function
const store = Redux.createStore(counterReducer)
// 2) Subscribe to redraw whenever the data changes in the future
store.subscribe(render)
// Our "user interface" is some text in a single HTML element
const valueEl = document.getElementById('value')
// 3) When the subscription callback runs:
function render() {
// 3.1) Get the current store state
const state = store.getState()
// 3.2) Extract the data you want
const newValue = state.value.toString()
// 3.3) Update the UI with the new value
valueEl.innerHTML = newValue
}
// 4) Display the UI with the initial store state
render()
// 5) Dispatch actions based on UI inputs
document.getElementById('increment').addEventListener('click', function () {
store.dispatch({ type: 'counter/incremented' })
})
No importa qué capa de UI uses, Redux funciona así con cualquier interfaz. Las implementaciones reales suelen ser un poco más complejas para optimizar el rendimiento, pero los pasos son los mismos cada vez.
Como Redux es una biblioteca independiente, existen diferentes bibliotecas de "conexión" para ayudarte a usar Redux con un framework de UI determinado. Esas bibliotecas de conexión manejan los detalles de suscribirse al almacén y actualizar eficientemente la UI cuando cambia el estado, para que no tengas que escribir ese código tú mismo.
Uso de Redux con React
La biblioteca oficial de enlace de UI React-Redux es un paquete separado del núcleo de Redux. Deberás instalarlo adicionalmente:
npm install react-redux
En este tutorial, cubriremos los patrones y ejemplos más importantes que necesitas para usar React y Redux juntos, y veremos cómo funcionan en la práctica como parte de nuestra aplicación de tareas pendientes.
Consulta la documentación oficial de React-Redux en https://react-redux.js.org para obtener una guía completa sobre cómo usar Redux y React juntos, y documentación de referencia sobre las APIs de React-Redux.
Diseñando el árbol de componentes
Al igual que diseñamos la estructura del estado en función de los requisitos, también podemos diseñar el conjunto general de componentes de UI y cómo se relacionan entre sí en la aplicación.
Basándonos en la lista de requisitos funcionales de la aplicación, como mínimo necesitaremos este conjunto de componentes:
<App>: el componente raíz que renderiza todo lo demás.<Header>: contiene el campo de texto "nueva tarea" y el checkbox "completar todas las tareas"<TodoList>: una lista de todos los elementos de tareas pendientes visibles actualmente, basada en los resultados filtrados<TodoListItem>: un elemento individual de la lista de tareas, con un checkbox que se puede hacer clic para alternar el estado completado de la tarea, y un selector de categoría por color
<Footer>: muestra el número de tareas activas y controles para filtrar la lista según el estado de completado y la categoría de color
Más allá de esta estructura básica de componentes, podríamos dividirlos de varias maneras. Por ejemplo, el componente <Footer> podría ser un único componente más grande, o podría tener varios componentes más pequeños dentro como <CompletedTodos>, <StatusFilter> y <ColorFilters>. No hay una única forma correcta de dividirlos, y descubrirás que puede ser mejor escribir componentes más grandes o dividirlos en muchos componentes pequeños según tu situación.
Por ahora, comenzaremos con esta pequeña lista de componentes para mantenerlo más fácil de seguir. Dicho esto, dado que asumimos que ya conoces React, nos saltaremos los detalles sobre cómo escribir el código de diseño de estos componentes y nos centraremos en cómo usar realmente la biblioteca React-Redux en tus componentes de React.
Esta es la UI inicial de React de esta aplicación antes de comenzar a agregar cualquier lógica relacionada con Redux:
Leyendo el estado del store con useSelector
Sabemos que necesitamos poder mostrar una lista de elementos de tareas pendientes. Comencemos creando un componente <TodoList> que pueda leer la lista de tareas del store, iterar sobre ellas y mostrar un componente <TodoListItem> por cada entrada de tarea.
Deberías estar familiarizado con hooks de React como useState, que se pueden llamar en componentes funcionales de React para darles acceso a valores de estado. React también nos permite escribir hooks personalizados, que nos permiten extraer lógica reutilizable para agregar nuestro propio comportamiento sobre los hooks integrados de React.
Como muchas otras bibliotecas, React-Redux incluye sus propios hooks personalizados, que puedes usar en tus componentes. Los hooks de React-Redux permiten que tus componentes de React se comuniquen con el store de Redux leyendo el estado y despachando acciones.
El primer hook de React-Redux que veremos es el hook useSelector, que permite a tus componentes de React leer datos del store de Redux.
useSelector acepta una única función, que llamamos función selector. Un selector es una función que toma todo el estado del store de Redux como argumento, lee algún valor del estado y devuelve ese resultado.
Por ejemplo, sabemos que el estado de Redux en nuestra app de tareas almacena el array de elementos como state.todos. Podemos escribir una pequeña función selector que devuelva ese array de tareas:
const selectTodos = state => state.todos
O quizá queramos averiguar cuántas tareas están marcadas actualmente como "completed" (completadas):
const selectTotalCompletedTodos = state => {
const completedTodos = state.todos.filter(todo => todo.completed)
return completedTodos.length
}
Por tanto, los selectores pueden devolver valores directamente del estado de Redux, pero también valores derivados basados en ese estado.
Vamos a leer el array de tareas en nuestro componente <TodoList>. Primero, importaremos el hook useSelector de la librería react-redux, y luego lo llamaremos pasándole una función selector como argumento:
import React from 'react'
import { useSelector } from 'react-redux'
import TodoListItem from './TodoListItem'
const selectTodos = state => state.todos
const TodoList = () => {
const todos = useSelector(selectTodos)
// since `todos` is an array, we can loop over it
const renderedListItems = todos.map(todo => {
return <TodoListItem key={todo.id} todo={todo} />
})
return <ul className="todo-list">{renderedListItems}</ul>
}
export default TodoList
La primera vez que el componente <TodoList> se renderice, el hook useSelector llamará a selectTodos y le pasará todo el objeto de estado de Redux. Lo que devuelva el selector será lo que el hook devuelva a tu componente. Así, const todos en nuestro componente acabará conteniendo el mismo array state.todos de nuestro estado de Redux.
Pero, ¿qué pasa si despachamos una acción como {type: 'todos/todoAdded'}? El estado de Redux se actualizará mediante el reducer, pero nuestro componente necesita saber que algo ha cambiado para poder volver a renderizarse con la nueva lista de tareas.
Sabemos que podemos llamar a store.subscribe() para escuchar cambios en el store, así que podríamos intentar escribir código para suscribirnos al store en cada componente. Pero eso rápidamente sería muy repetitivo y difícil de manejar.
Afortunadamente, ¡useSelector se suscribe automáticamente al store de Redux por nosotros! Así, cada vez que se despacha una acción, volverá a llamar inmediatamente a su función selector. Si el valor devuelto por el selector cambia respecto a la última ejecución, useSelector forzará a nuestro componente a volver a renderizarse con los nuevos datos. Todo lo que tenemos que hacer es llamar a useSelector() una vez en nuestro componente, y él hace el resto del trabajo.
Sin embargo, hay algo muy importante que recordar:
¡useSelector compara sus resultados usando comparaciones estrictas de referencia ===, por lo que el componente se volverá a renderizar cada vez que el resultado del selector sea una nueva referencia! Esto significa que si creas una nueva referencia en tu selector y la devuelves, tu componente podría volver a renderizarse cada vez que se despache una acción, incluso si los datos realmente no han cambiado.
Por ejemplo, pasar este selector a useSelector hará que el componente se vuelva a renderizar siempre, porque array.map() siempre devuelve una nueva referencia de array:
// Bad: always returning a new reference
const selectTodoDescriptions = state => {
// This creates a new array reference!
return state.todos.map(todo => todo.text)
}
Hablaremos de una forma de solucionar este problema más adelante en esta sección. También veremos cómo mejorar el rendimiento y evitar renderizados innecesarios usando funciones selectoras "memoized" en Parte 7: Patrones estándar de Redux.
También vale la pena mencionar que no necesitas escribir una función selector como variable separada. Puedes escribirla directamente dentro de la llamada a useSelector, así:
const todos = useSelector(state => state.todos)
Despachando acciones con useDispatch
Ya sabemos cómo leer datos del store de Redux en nuestros componentes. Pero, ¿cómo podemos despachar acciones al store desde un componente? Sabemos que fuera de React podemos llamar a store.dispatch(action). Como no tenemos acceso al store en un archivo de componente, necesitamos una forma de acceder a la función dispatch dentro de nuestros componentes.
El hook useDispatch de React-Redux nos devuelve el método dispatch del store. (De hecho, la implementación del hook es literalmente return store.dispatch.)
Por tanto, podemos llamar a const dispatch = useDispatch() en cualquier componente que necesite despachar acciones, y luego llamar a dispatch(someAction) según sea necesario.
Probemos esto en nuestro componente <Header>. Sabemos que debemos permitir que el usuario escriba texto para un nuevo elemento de tareas pendientes, y luego despachar una acción {type: 'todos/todoAdded'} que contenga ese texto.
Escribiremos un componente de formulario típico de React que use "inputs controlados" para permitir que el usuario escriba el texto. Luego, cuando el usuario presione específicamente la tecla Enter, despacharemos esa acción.
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
const Header = () => {
const [text, setText] = useState('')
const dispatch = useDispatch()
const handleChange = e => setText(e.target.value)
const handleKeyDown = e => {
const trimmedText = e.target.value.trim()
// If the user pressed the Enter key:
if (e.key === 'Enter' && trimmedText) {
// Dispatch the "todo added" action with this text
dispatch({ type: 'todos/todoAdded', payload: trimmedText })
// And clear out the text input
setText('')
}
}
return (
<input
type="text"
placeholder="What needs to be done?"
autoFocus={true}
value={text}
onChange={handleChange}
onKeyDown={handleKeyDown}
/>
)
}
export default Header
Pasando el Store con Provider
Nuestros componentes ya pueden leer el estado del store y despachar acciones. Sin embargo, aún falta algo: ¿dónde y cómo encuentran los hooks de React-Redux el store correcto? Un hook es una función JavaScript, por lo que no puede importar automáticamente un store desde store.js por sí solo.
En su lugar, debemos indicar explícitamente a React-Redux qué store queremos usar en nuestros componentes. Lo hacemos envolviendo toda nuestra aplicación <App> con un componente <Provider> y pasando el store de Redux como prop a <Provider>. Tras hacer esto una vez, cada componente de la aplicación podrá acceder al store de Redux si lo necesita.
Añadamos esto a nuestro archivo principal index.js:
import React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'
import App from './App'
import store from './store'
const root = createRoot(document.getElementById('root'))
root.render(
// Render a `<Provider>` around the entire `<App>`,
// and pass the Redux store to it as a prop
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
)
Esto cubre las partes clave de usar React-Redux con React:
-
Llama al hook
useSelectorpara leer datos en componentes React -
Llama al hook
useDispatchpara despachar acciones en componentes React -
Envuelve todo tu componente
<App>con<Provider store={store}>para que otros componentes puedan comunicarse con el store
¡Ahora deberíamos poder interactuar con la aplicación! Esta es la interfaz funcional hasta ahora:
Veamos ahora un par de formas adicionales de usar estos elementos juntos en nuestra app de tareas.
Patrones de React-Redux
Estado Global, Estado de Componente y Formularios
A estas alturas te estarás preguntando: "¿Siempre tengo que poner todo el estado de mi aplicación en la tienda Redux?"
La respuesta es NO. El estado global necesario en toda la aplicación debe ir en la tienda Redux. El estado que solo se necesita en un lugar debe mantenerse en el estado del componente.
Un buen ejemplo es el componente <Header> que escribimos antes. Podríamos guardar el texto actual del input en el store de Redux, despachando una acción en el manejador onChange del input y guardándolo en nuestro reducer. Pero eso no nos aporta ningún beneficio. El único lugar donde se usa ese texto es aquí, en el componente <Header>.
Por tanto, tiene sentido mantener ese valor en un hook useState dentro del componente <Header>.
Del mismo modo, si tuviéramos un flag booleano llamado isDropdownOpen, a ningún otro componente de la app le importaría; debería permanecer local a este componente.
En una aplicación React + Redux, tu estado global debe ir en el store de Redux, y tu estado local debe permanecer en los componentes React.
Si no estás seguro de dónde colocar algo, aquí tienes algunas reglas generales para determinar qué tipo de datos deben ir en Redux:
- ¿Le importan estos datos a otras partes de la aplicación?
- ¿Necesitas poder crear datos derivados basados en estos datos originales?
- ¿Se usan los mismos datos para impulsar múltiples componentes?
- ¿Tiene valor poder restaurar este estado a un punto específico en el tiempo (por ejemplo, depuración con viaje en el tiempo)?
- ¿Quieres almacenar en caché estos datos (es decir, usar lo que ya está en el estado en lugar de volver a solicitarlos)?
- ¿Quieres mantener estos datos consistentes durante la recarga en caliente de componentes (que podrían perder su estado interno al intercambiarse)?
Este también es un buen ejemplo de cómo pensar en formularios con Redux en general. La mayoría del estado de formularios probablemente no debería guardarse en Redux. En su lugar, mantén los datos en tus componentes de formulario mientras se editan, y luego despacha acciones de Redux para actualizar el store cuando el usuario termine.
Uso de múltiples selectores en un componente
Actualmente, solo nuestro componente <TodoList> lee datos del store. Veamos cómo sería que el componente <Footer> también comience a leer algunos datos.
El <Footer> necesita conocer tres piezas de información distintas:
-
Cuántos todos completados hay
-
El valor actual del filtro de "estado"
-
La lista actual de filtros de categoría de "color" seleccionados
¿Cómo podemos leer estos valores en el componente?
Podemos llamar a useSelector múltiples veces dentro de un mismo componente. De hecho, esta es una buena idea: cada llamada a useSelector siempre debe devolver la mínima cantidad de estado posible.
Ya vimos antes cómo escribir un selector que cuenta los todos completados. Para los valores de filtros, tanto el valor del filtro de estado como los valores de los filtros de color viven en el slice state.filters. Como este componente necesita ambos, podemos seleccionar todo el objeto state.filters.
Como mencionamos antes, podríamos poner todo el manejo de entradas directamente en <Footer>, o dividirlo en componentes separados como <StatusFilter>. Para abreviar, omitiremos los detalles exactos del manejo de entradas y asumiremos que tenemos componentes separados más pequeños a los que se les pasan algunos datos y callbacks manejadores de cambios como props.
Dada esa suposición, las partes de React-Redux del componente podrían verse así:
import React from 'react'
import { useSelector } from 'react-redux'
import { availableColors, capitalize } from '../filters/colors'
import { StatusFilters } from '../filters/filtersSlice'
// Omit other footer components
const Footer = () => {
const todosRemaining = useSelector(state => {
const uncompletedTodos = state.todos.filter(todo => !todo.completed)
return uncompletedTodos.length
})
const { status, colors } = useSelector(state => state.filters)
// omit placeholder change handlers
return (
<footer className="footer">
<div className="actions">
<h5>Actions</h5>
<button className="button">Mark All Completed</button>
<button className="button">Clear Completed</button>
</div>
<RemainingTodos count={todosRemaining} />
<StatusFilter value={status} onChange={onStatusChange} />
<ColorFilters value={colors} onChange={onColorChange} />
</footer>
)
}
export default Footer
Selección de datos en elementos de lista por ID
Actualmente, nuestro <TodoList> está leyendo todo el array state.todos y pasando los objetos todo reales como prop a cada componente <TodoListItem>.
Esto funciona, pero hay un problema potencial de rendimiento.
-
Cambiar un objeto todo implica crear copias tanto del todo como del array
state.todos, y cada copia es una nueva referencia en memoria -
Cuando
useSelectorve una nueva referencia como resultado, fuerza a su componente a volver a renderizarse -
Por lo tanto, cada vez que se actualiza un objeto todo (como hacer clic para alternar su estado completado), todo el componente padre
<TodoList>se volverá a renderizar -
Luego, debido a que React vuelve a renderizar todos los componentes hijos recursivamente por defecto, ¡también significa que todos los componentes
<TodoListItem>se volverán a renderizar, aunque la mayoría no haya cambiado realmente!
Volver a renderizar componentes no es malo: así es como React sabe si necesita actualizar el DOM. Pero volver a renderizar muchos componentes cuando nada ha cambiado puede ralentizar demasiado la aplicación si la lista es muy grande.
Hay un par de formas de solucionar esto. Una opción es envolver todos los componentes <TodoListItem> en React.memo(), para que solo se vuelvan a renderizar cuando sus props cambien realmente. Esta suele ser una buena opción para mejorar el rendimiento, pero requiere que el componente hijo siempre reciba las mismas props hasta que algo cambie realmente. Como cada componente <TodoListItem> recibe un elemento todo como prop, solo uno de ellos debería recibir una prop cambiada y tener que volverse a renderizar.
Otra opción es que el componente <TodoList> solo lea un array de IDs de todos del store, y pase esos IDs como props a los componentes hijos <TodoListItem>. Luego, cada <TodoListItem> puede usar ese ID para encontrar el objeto todo que necesita.
Probemos este enfoque.
import React from 'react'
import { useSelector } from 'react-redux'
import TodoListItem from './TodoListItem'
const selectTodoIds = state => state.todos.map(todo => todo.id)
const TodoList = () => {
const todoIds = useSelector(selectTodoIds)
const renderedListItems = todoIds.map(todoId => {
return <TodoListItem key={todoId} id={todoId} />
})
return <ul className="todo-list">{renderedListItems}</ul>
}
Esta vez, solo seleccionamos un array de IDs de tareas desde el store en <TodoList>, y pasamos cada todoId como prop id a los componentes hijos <TodoListItem>.
Luego, en <TodoListItem>, podemos usar ese valor de ID para leer nuestro elemento de tarea. También podemos actualizar <TodoListItem> para despachar la acción "toggled" basándonos en el ID de la tarea.
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { availableColors, capitalize } from '../filters/colors'
const selectTodoById = (state, todoId) => {
return state.todos.find(todo => todo.id === todoId)
}
// Destructure `props.id`, since we only need the ID value
const TodoListItem = ({ id }) => {
// Call our `selectTodoById` with the state _and_ the ID value
const todo = useSelector(state => selectTodoById(state, id))
const { text, completed, color } = todo
const dispatch = useDispatch()
const handleCompletedChanged = () => {
dispatch({ type: 'todos/todoToggled', payload: todo.id })
}
// omit other change handlers
// omit other list item rendering logic and contents
return (
<li>
<div className="view">{/* omit other rendering output */}</div>
</li>
)
}
export default TodoListItem
Sin embargo, hay un problema con esto. Dijimos antes que devolver nuevas referencias de arrays en los selectores hace que los componentes se vuelvan a renderizar cada vez, y justo ahora estamos devolviendo un nuevo array de IDs en <TodoList>. En este caso, el contenido del array de IDs debería ser el mismo si estamos cambiando el estado de una tarea, porque seguimos mostrando los mismos elementos de tarea — no hemos añadido ni eliminado ninguno. Pero, el array que contiene esos IDs es una nueva referencia, por lo que <TodoList> se volverá a renderizar cuando realmente no necesita hacerlo.
Una posible solución a esto es cambiar cómo useSelector compara sus valores para ver si han cambiado. useSelector puede aceptar una función de comparación como segundo argumento. Una función de comparación se llama con los valores antiguo y nuevo, y devuelve true si se consideran iguales. Si son iguales, useSelector no hará que el componente se vuelva a renderizar.
React-Redux tiene una función de comparación shallowEqual que podemos usar para comprobar si los elementos dentro del array siguen siendo los mismos. Probemos eso:
import React from 'react'
import { useSelector, shallowEqual } from 'react-redux'
import TodoListItem from './TodoListItem'
const selectTodoIds = state => state.todos.map(todo => todo.id)
const TodoList = () => {
const todoIds = useSelector(selectTodoIds, shallowEqual)
const renderedListItems = todoIds.map(todoId => {
return <TodoListItem key={todoId} id={todoId} />
})
return <ul className="todo-list">{renderedListItems}</ul>
}
Ahora, si cambiamos el estado de un elemento de tarea, la lista de IDs se considerará la misma, y <TodoList> no tendrá que volverse a renderizar. El componente <TodoListItem> correspondiente obtendrá un objeto de tarea actualizado y se volverá a renderizar, pero todos los demás seguirán teniendo el objeto de tarea existente y no tendrán que volverse a renderizar en absoluto.
Como se mencionó anteriormente, también puedes usar un tipo especial de función selector llamada un "selector memoizado" para ayudar a mejorar el renderizado de componentes, y veremos cómo usarlos en otra sección.
Lo que has aprendido
¡Ahora tenemos una app de tareas funcional! Nuestra aplicación crea un store, pasa el store a la capa de UI de React usando <Provider>, y luego llama a useSelector y useDispatch para comunicarse con el store en nuestros componentes de React.
¡Intenta implementar el resto de las funcionalidades de UI que faltan por tu cuenta! Aquí tienes una lista de lo que necesitarás añadir:
- En el componente
<TodoListItem>, usa el hookuseDispatchpara despachar acciones para cambiar la categoría de color y eliminar la tarea. - En
<Footer>, usa el hookuseDispatchpara despachar acciones para marcar todas las tareas como completadas, limpiar las tareas completadas y cambiar los valores del filtro.
Cubriremos la implementación de los filtros en Parte 7: Patrones estándar de Redux.
Veamos cómo se ve la aplicación ahora, incluyendo los componentes y secciones que omitimos para mantener esto más breve:
- Los stores de Redux pueden usarse con cualquier capa de UI
- El código de UI siempre se suscribe al store, obtiene el último estado y se redibuja a sí mismo
- React-Redux es la biblioteca oficial de vinculación de UI de Redux para React
- React-Redux se instala como un paquete separado
react-redux
- React-Redux se instala como un paquete separado
- El hook
useSelectorpermite a los componentes de React leer datos del store- Las funciones selector toman todo el
statedel store como argumento y devuelven un valor basado en ese estado useSelectorllama a su función selector y devuelve el resultado del selectoruseSelectorse suscribe al store y vuelve a ejecutar el selector cada vez que se despacha una acción- Cada vez que el resultado del selector cambia,
useSelectorfuerza al componente a volverse a renderizar con los nuevos datos
- Las funciones selector toman todo el
- El hook
useDispatchpermite a los componentes de React despachar acciones al storeuseDispatchdevuelve la función realstore.dispatch- Puedes llamar a
dispatch(action)según sea necesario dentro de tus componentes
- El componente
<Provider>hace que el store esté disponible para otros componentes de React- Renderiza
<Provider store={store}>alrededor de toda tu<App>
- Renderiza
¿Qué sigue?
Ahora que nuestra interfaz de usuario funciona, es hora de ver cómo hacer que nuestra aplicación Redux se comunique con un servidor. En Parte 6: Lógica asíncrona, veremos cómo la lógica asíncrona como timeouts y peticiones HTTP encajan en el flujo de datos de Redux.