Redux con React

Redux no tiene relación con React. Puedes escribir aplicaciones Redux con React, Angular, Ember, jQuery o JavaScript puro.

Dicho esto, Redux funciona especialmente bien con bibliotecas como React y Deku porque te permiten describir la interfaz de usuario como una función de estado, y Redux emite actualizaciones de estado en respuesta a las acciones.

Para poder usar Redux con React necesitamos instalar el paquete npm npm install --save react-redux.

Cambio de paradigma

A partir de ahora, tendremos dos tipos de componentes, los componetes de representación y los componentes contenedores - más detalles en este artículo en medium de Dan Abramov.

Componentes de presentación Componentes contenedor
Propósito Cómo se ven las cosas (marcado, estilos) Cómo funcionan las cosas (búsqueda de datos, actualizaciones de estado)
Consciente de Redux No
Lectura de datos Lee de props Se suscribe al estado Redux
Modificar datos Callbacks en props Dispatch acciones Redux

Componentes de presentación

Describen la apariencia pero no saben de dónde vienen los datos, ni cómo cambiarlos. Si migra de Redux a otra cosa, podrá mantener todos estos componentes exactamente iguales. No tienen dependencia de Redux.

import React, { PropTypes } from 'react'
import Todo from './Todo'

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

TodoList.propTypes = {
  todos: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
  }).isRequired).isRequired,
  onTodoClick: PropTypes.func.isRequired
}

export default TodoList

Componentes contenedor

Técnicamente, un componente de contenedor es sólo un componente React que utiliza store.subscribe() para leer una parte del árbol de estado Redux y suministrar props a un componente de presentación que lo procesa. Se puede escribir un componente de contenedor manualmente, pero Redux sugiere generar componentes de contenedor con la función connect() de la biblioteca react-redux que proporciona muchas optimizaciones útiles para evitar re-renders innecesarios.

Para usar connect(), debe definir una función especial llamada mapStateToProps que indique cómo transformar el estado de store Redux actual en parte de las props que desea transferir a un componente de presentación.

Además de leer el estado, los componentes contenedor pueden enviar acciones. De manera similar a mapStateToProps, se puede definir una función llamada mapDispatchToProps() que recibe el método dispatch() y devuelve los props con callbacks que queremos inyectar en el componente de presentación.

import { connect } from 'react-redux'

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
  }
}

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList

Conectar con el Store

Todos los componentes contenedor necesitan acceso al store de Redux para que puedan suscribirse. Una opción sería pasarlo como un propiedad a cada componente contenedor. Pfff 😰 eso seria, como poco tedioso.

La opción que recomendamos es utilizar un componente de react-redux especial denominado <Provider> para que el store esté disponible de forma mágica para todos los componentes contenedor en la aplicación sin pasarlo explícitamente. Sólo es necesario utilizarlo una vez al renderizar el componente raíz:

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

results matching ""

    No results matching ""