MAnast
MAnast

Reputation: 151

How immer push in an array triggers an update in redux

I noticed that immer doesn't create a new array when doing draft.list.push()

Why this is a suggested approach when this doesn't work for redux? Is spread operator the only solution to this? The actual gain from this is that we are sure that array objects will not be mutated?

I am a little confused of the actual purpose of this action

Upvotes: 1

Views: 3614

Answers (1)

HMR
HMR

Reputation: 39320

Without seeing any code it is impossible to say what's wrong but there is nothing wrong with immer using push:

const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;
const { produce } = immer;

const id = ((id) => () => id++)(4);
const initialState = {
  data: {
    counters: [
      { id: 1, count: 0 },
      { id: 2, count: 0 },
      { id: 3, count: 0 },
    ],
  },
};
//action types
const INCREASE = 'INCREASE';
const ADD = 'ADD';
//action creators
const increase = (id) => ({
  type: INCREASE,
  payload: id,
});
const add = () => ({
  type: ADD,
});
const reducer = (
  state = initialState,
  { type, payload }
) => {
  if (type === INCREASE) {
    const index = state.data.counters.findIndex(
      ({ id }) => id === payload
    );
    //you sure you return here?
    return produce(state, (draft) => {
      ++draft.data.counters[index].count;
    });
  }
  if (type === ADD) {
    //returning new state
    return produce(state, (draft) => {
      //using arry push on immer draft
      draft.data.counters.push({ id: id(), count: 0 });
    });
  }
  return state;
};
//selectors
const selectCounters = (state) => state.data.counters;
const createSelectCounterById = (counterId) =>
  createSelector([selectCounters], (counters) =>
    counters.find(({ id }) => id === counterId)
  );
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(() => (next) => (action) =>
      next(action)
    )
  )
);

const Counter = React.memo(function Counter({ id }) {
  const dispatch = useDispatch();
  const selectCounter = React.useMemo(
    () => createSelectCounterById(id),
    [id]
  );
  const counter = useSelector(selectCounter);
  console.log('rendering:', id);
  return (
    <li>
      {id}:{' '}
      <button onClick={() => dispatch(increase(id))}>
        {counter.count}
      </button>
    </li>
  );
});
const App = () => {
  const counters = useSelector(selectCounters);
  const dispatch = useDispatch();
  return (
    <div>
      <button onClick={() => dispatch(add())}>
        add counter
      </button>
      <ul>
        {counters.map((counter) => (
          <Counter key={counter.id} id={counter.id} />
        ))}
      </ul>
    </div>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/immer.umd.production.min.js"></script>
<div id="root"></div>

Upvotes: 1

Related Questions