Contribuye

Solución de problemas

Este un un lugar para compartir soluciones a problemas comunes. Los ejemplos usan React, pero deberías encontrarlos útiles incluso si usas otra cosa.

No ocurre nada el despachar una acción

Algunas veces, tratas de despachar una acción, pero la vista no se actualiza. ¿Por qué ocurre esto? Hay varias razons.

Nunca modifiques los argumentos del reducer

Si estas tratando de modificar el state o action que te da Redux. ¡No lo hagas!

Redux asume que nunca modificas el estado que te da en los reducers. Siempre debes devolver un nuevo objeto con el estado. Incluso si no usas librerías como https://facebook.github.io/immutable-js/, necesitas evitar completamente las modificaciones.

La inmutabilidad es lo que permite a react-redux suscribirse eficientemente a actualizaciones muy específicas de tu estado. También permite un gran experiencia de desarrollo gracias a características como time travel con redux-devtools.

Por ejemplo, un reducer como este está mal porque modifica el estado:

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      // Mal! Esto modifica el estado
      state.push({
        text: action.text,
        completed: false
      })
      return state
    case 'COMPLETE_TODO':
      // Mal! Esto modifica state[action.index]
      state[action.index].completed = true
      return state
    default:
      return state
  }
}

Debe ser re programado de esta forma:

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      // Devuelve un nuevo array
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case 'COMPLETE_TODO':
      // Devuelve un nuevo array
      return [
        ...state.slice(0, action.index),
        // Copia el objeto antes de modificarlo
        Object.assign({}, state[action.index], {
          completed: true
        }),
        ...state.slice(action.index + 1)
      ]
    default:
      return state
  }
}

Es más código, pero es lo que permite a Redux ser predecible y eficiente. si quieres tener menos código puedes usar herramientas como React.addons.update para escribir transformaciones inmutables con una sintaxis pequeña:

// Antes:
return [
  ...state.slice(0, action.index),
  Object.assign({}, state[action.index], {
    completed: true,
  }),
  ...state.slice(action.index + 1),
];

// Después:
return update(state, {
  [action.index]: {
    completed: {
      $set: true,
    },
  },
});

Por último, para actualizar objeto, vas a necesitar algo como _.extend de Lodash o mejor, un polyfill de `Object.assign.

Asegurate de que usas Object.assign correctamente. Por ejemplo, en vez de devolver algo como Object.assign(state, newData) en tus reducers, devuelve Object.assign({}, state, newData). De estas forma no vas a modificar el state anterior.

También puedes habilitar ES7 object spread proposal con Babel stage 1:

// Antes:
return [
  ...state.slice(0, action.index),
  Object.assign({}, state[action.index], {
    completed: true,
  }),
  ...state.slice(action.index + 1),
];

// Después:
return [
  ...state.slice(0, action.index),
  { ...state[action.index], completed: true },
  ...state.slice(action.index + 1),
];

Ten en cuenta que las funciones experimentales estás sujetas a cambios, y no es bueno depender de ellas en grandes cantidades de código.

No olvides llamar dispatch(action)

Sí defines un creador de acciones, ejecutándolo no va a despachar tus acciones automáticamente. Por ejemplo, este código no hace nada:

TodoActions.js

export function addTodo(text) {
  return { type: 'ADD_TODO', text };
};

AddTodo.js

import React, { Component } from 'react';
import { addTodo } from './TodoActions';

class AddTodo extends Component {
  handleClick() {
    // ¡No funciona!
    addTodo('Fix the issue');
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>
        Add
      </button>
    );
  }
}

No funciona ya que el creador de acciones es solo una función que devuelve una acción. Pero depende de vos despacharla.No podemos conectar tu creador de acciones a una instancia específica del Store durante su definición ya que las aplicaciones que renderizas en el servidor necesitas un store de Redux para cada petición.

Esto se arreglar ejecutando el método dispatch() de la instancia del store:

handleClick() {
  // ¡Funciona! (pero debes obtener el store de alguna forma)
  store.dispatch(addTodo('Fix the issue'));
}

Si estas en algún punto profundo en la jerarquía de componentes, es incómodo pasar el store manualmente. Es por eso que react-redux te permite usar el componente de nivel superior connect que va a además de suscribirse al store de Redux, inyectar dispatch en los props de tus componentes.

El código arreglado quedaría así:

AddTodo.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { addTodo } from './TodoActions';

class AddTodo extends Component {
  handleClick() {
    // ¡Funciona!
    this.props.dispatch(addTodo('Fix the issue'));
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}>
        Add
      </button>
    )
  }
}

// Además del state, `connect` agregar `dispatch` en nuestros props.
export default connect()(AddTodo);

Puedes pasar dispatch a otros componentes manualmente, si quieres.

Algo más no funciona

Pregunta en el canal #redux con Reactiflux, o crea un issue. Si lo descubre, modifica este documento como cortesía a la siguiente persona con el mismo problema.