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>
我注意到 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>