5

I think equalityFn in useSelector doesn't work as it should. It works great with simple strings/numbers but it doesn't work with objects and arrays. It always gets 'item' and 'prevItem' equal in this case. Check example here:

import React, { Component } from 'react';
import { render } from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { connect, Provider, useSelector, useDispatch } from 'react-redux';
import './style.css';

const countReducer = (state = { count: [] }, action) => {
  switch (action.type) {
    case 'INC': 
      state.count.push('1')
      return state;
    default: return state;
  }
}

const reducers = combineReducers({
  counter: countReducer,
})

const store = createStore(reducers);

function App () {
  const dispatch = useDispatch()
  const count = useSelector(state => state.counter.count, (item, previousItem) => {
    console.log('old')
    console.log(previousItem)
    console.log('new')
    console.log(item)
    if (item.length === previousItem.length) {
      console.log('Equal')
      return true
    } else {
      console.log('Not Equal')
      return false
    }
  })
  return (
    <div>
      <button onClick={() => dispatch({
        type: 'INC'
      })}>Increment</button>
      <div>Value: {count[0]}</div>
    </div>
  )
}

const AppContainer = connect()(App);

render(
  <Provider store={store}>
    <AppContainer />
  </Provider>,
  document.getElementById('root')
);
Ramesh R
  • 7,009
  • 4
  • 25
  • 38
Nick
  • 61
  • 1
  • 3

1 Answers1

8
state.count.push('1')

Do not mutate the state.

One of redux's core concepts is that the state is immutable. Since the state is assumed to be immutable, a super-quick reference equality check (ie, ===) is all that's needed to tell whether the state has changed. If you violate that assumption by mutating the state, then the state before and the state after are the same object, and so it looks like nothing has changed when in fact it has.

Instead, create a new array. With the spread syntax, you can do so like this:

case 'INC': 
  return {
    ...state,
    count: [...state.count, '1']
  }

See also their recommendations on this page: Immutable Update Patterns

Tobias Tengler
  • 6,848
  • 4
  • 20
  • 34
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98