immer push 数组如何触发 redux 中的更新

How immer push in an array triggers an update in redux

我注意到 immer 在执行 draft.list.push()

时不会创建新数组

当这不适用于 redux 时,为什么这是建议的方法?传播运营商是唯一的解决方案吗?这样做的实际收获是我们确定数组对象不会发生变异?

我有点不明白这个动作的实际目的

在没有看到任何代码的情况下,无法说出哪里出了问题,但是使用 push immer 没有任何问题:

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/immer@7.0.5/dist/immer.umd.production.min.js"></script>
<div id="root"></div>