Un contenedor predecible del estado global de una aplicación en Javascript.
Basada en la arquitectura Flux de Facebook. Pero más sencilla.
No. Redux es una libería externa.
Puedes usarlo combinado con React, o cualquier otra librería de vistas. Sólo ocupa 2kB y no tiene dependencias.
Redux es la librería más elegida en proyectos de React a la hora de abordar el problema del estado de la aplicación pero no es la única.
{
todos: [{
text: 'Comer',
completed: true
}, {
text: 'Hacer ejercicio',
completed: false
}],
visibilityFilter: 'SHOW_ALL'
}Estado de la aplicación
{
todos: [{
text: 'Comer',
completed: true
}, {
text: 'Hacer ejercicio',
completed: false
}],
visibilityFilter: 'SHOW_ALL'
}Estado global
{
type: 'ADD_TODO',
text: 'Ir a nadar a la piscina'
}{
type: 'TOGGLE_TODO',
index: 1
}Acción
Acción
1. Única fuente de la verdad
Todo el estado de tu aplicación esta contenido en un único store
2. El estado es de sólo lectura (inmutable)
La única forma de modificar el estado es emitir una action que indique que cambió
3. Los cambios se realizan con funciones puras.
Para controlar como el store es modificado por las acciones se usan reducers puros
Store
View
subscribe
Actions
dispatch
Reducers
Las Actions son objetos de Javascript que tienen una propiedad "type" que indica el tipo de acción. Pueden contener otras propiedades que sirven como "payload" para poder realizar la acción.
{
type: 'ADD_TODO',
text: 'Aprender Redux'
}Funciones puras que devuelven Actions. Son útiles para evitar inconsistencias en el código y tener que escribir los objetos a mano.
// esta función es un ActionCreator,
// ya que devuelve un objeto que es un Action
function addTodo(text) {
return {
type: 'ADD_TODO',
payload: {
text,
},
}
}Función pura que recibe el estado anterior y la acción y, a partir de ellas, crea un nuevo estado de la aplicación.
(previousState, action) => nextStatefunction todoApp(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
default:
return state
}
}Almacena el estado global de la aplicación. Permite que el estado sea leído, que te puedas suscribir a sus cambios y, lo más importante, que envíes acciones para crear un nuevo estado global.
// cargamos la función para crear un store
import { createStore } from 'redux'
// cargamos nuestros reducers
import reducers from './reducers'
// creamos el store
const store = createStore(reducers)// archivo: store.js
// importamos el método createStore desde redux
import { createStore } from 'redux'
// creamos nuestro reducer
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}
// creamos la store, pasándole el reducer
const store = createStore(todos)
// exportamos la store
export default storeimport store from './store.js';
// muestra el estado global actual de la aplicación
console.log(store.getState());import store from './store.js';
// al suscribirte a la store, te devuelve
// una función para ejecutarla y desuscribirte más tarde
const unsubscribe = store.subscribe(() => {
// esta función se ejecuta cuando haya una actualización en el state
console.log(store.getState())
// podríamos aquí actualizar nuestra aplicación con la nueva info
const { usuario } = store.getState()
document.getElementById('usuario').innerHTML = usuario
});
// podríamos usar el método unsubscribe más tarde
// para eliminar la suscripción a la store
document.getElementById('cerrar').addEventListener('click', () => unsubscribe())
import store from './store.js';
// Esto envía una acción a la store y actualizará
// el estado usando el reducer que hayas programado
store.dispatch({
type: 'ADD_TODO',
text: 'Aprender React con @midudev'
})
import store from './store.js';
import { addTodo } from './actions'
/** Importamos esta función de la anterior línea
function addTodo(text) {
return {
type: 'ADD_TODO',
payload: {
text,
},
}
**/
// Esto envía una acción a la store y actualizará
// el estado usando el reducer que hayas programado
// Esta vez usando un ActionCreator
store.dispatch(addTodo('Aprender React con @midudev'))Librería que facilita la conexión de React con Redux gracias a algunas utilidades que ofrece.
# instalando la dependencia en nuestro proyecto
npm install --save react-redux
react-redux sigue el patrón de contenedor-contenido de React. Esto es, separar los componentes presentacionales y de contenedor.
¡Si no tienes claro el concepto puedes volver al curso a revisarlo!
connect()
<Provider />
react-redux nos proporciona dos utilidades para crear la conexión entre Redux y nuestra aplicación de React de forma sencilla:
connect([mapStateToProps], [mapDispatchToProps])
Sirve para conectar un componente de React a la store de Redux.
Devuelve una función que recibe como parámetro el componente que queremos conectar.
connect([mapStateToProps], [mapDispatchToProps])
mapStateToProps:
Función que devuelve el trozo de estado al que queremos que el componente se suscriba. Es opcional. Si no se indica, el componente no se suscribirá a la store.
// recibe dos parámetros:
// [state] el estado global actual
// [ownProps] las props que recibe el componente
const mapStateToProps = (state, ownProps) => {
// en este caso, el componente recibirá "counter" como prop
return { counter: state.counter }
}connect([mapStateToProps], [mapDispatchToProps])
mapDispatchToProps:
Función que devuelve las acciones que podrá despachar nuestro componente a la store. Es la única forma cómo podremos generar una actualización del state de la store.
// recibe dos parámetros:
// [dispatch] método para llamar a las actions
// [ownProps] las props que ha recibido el componente
const mapDispatchToProps = (dispatch, ownProps) => {
// nombre de las props para ejecutar y llamar a una action
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
reset: () => dispatch({ type: 'RESET' })
}
}connect([mapStateToProps], [mapDispatchToProps])
const mapStateToProps = (state, ownProps) => {
// en este caso, el componente recibirá "counter" como prop
return { counter: state.counter }
}
const mapDispatchToProps = (dispatch, ownProps) => {
// nombre de las props para ejecutar y llamar a una action
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
reset: () => dispatch({ type: 'RESET' })
}
}
const createConnect = connect(
mapStateToProps,
mapDispatchToProps
)
const ComponentWithConnectionToRedux = createConnect(Counter)
export default ComponentWithConnectionToRedux<Provider store>
Hace que la store esté disponible en todo nuestro árbol de elementos. Tendremos que envolver nuestra aplicación con este componente para poder utilizar el método connect en cualquier descendiente.
<Provider store={store}>
import {counterReducers} from './reducers'
import Counter from './containers/Counter'
const store = createStore(counterReducers)
ReactDOM.render(
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById('root')
)