Search code examples
javascriptreduxreact-reduximmutable.jsreselect

Why should selectors always return immutable when using immutable data structures in redux.?


According to the redux FAQ from here https://github.com/reduxjs/redux/blob/master/docs/recipes/UsingImmutableJS.md#what-are-some-opinionated-best-practices-for-using-immutable-js-with-redux:

"Your selectors should return Immutable.JS objects". "Always".

Why is this the case?


Solution

  • As a disclaimer, this isn't "always" the case, but the docs are trying to point you in the right direction for most cases.

    Since reselect memoizes the return result of selectors, returning a mutable object leaves you susceptible to tricky bugs. Imagine the following scenario:

    // Immutable State
    {
      todos: [{ text: "hey"}, { todo: "text"}]
    }
    
    // Selectors
    
    const getTodos = createSelector(state => state.todos, immutableTodos => immutableTodos.toJS())
    

    The getTodos selector is returning a plain JS object, which by default is mutable. Now imagine multiple smart components that are using the getTodos selector.

    class EditTodos extends PureComponent {
      constructor(props) {
        this.state = { todos: props.todos }
      }
    
      addUnsavedTodo(newTodo) {
        // Accidentally mutate the return result of getTodos
        const newTodos = this.state.todos.push(newTodo)
        this.setState({ todos: newTodos })
      }
    
      render() { // Some components for adding unsaved todos }
    }
    
    const mapStateToProps = (state) => ({ todos: getTodos(state))
    

    A second component also using getTodos would see the new "unsaved" todo as soon as addUnsavedTodo is called, which would most likely be unintentional. All calls to getTodos, assuming redux state is unchanged, will get the same reference and any mutations will affect all consumers.

    The example above is contrived, but hopefully it demonstrates one of the reasons that returning plain JS objects can be risky.

    Furthermore, as the docs mention, you should limit your use of toJS since it has performance implications. There is no benefit to converting your immutable object into a plain JS object within a selector