reactjs - 当 Redux 存储发生变化时,阻止整个组件重新渲染
问题描述
我有一个功能组件,可以呈现带有行的表格。每一行都填充了作为数组存储在 Redux 存储中的数据。每行也可以被删除,如果在给定的时间范围内(通过吐司),用户也可以撤消删除。
我面临的问题是,当从 Redux 存储数组中删除 1 行的数据时,整个表都会重新渲染。撤消删除后将其添加回数组时也是如此。
我知道在每次删除/撤消时,都会创建一个新数组,该数组会保留在商店中,因此会重新渲染。但是有没有办法可以优化它,这样整个表就不会每次都重新渲染?
解决方案
您可以选择行组件中的每个项目,如果您重新创建所有项目,因为您重新获取它们然后添加比较功能:
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;
const initialState = {
items: [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 3, name: 'three' },
],
};
//action types
const REMOVE = 'REMOVE';
//action creators
const remove = (itemId) => ({
type: REMOVE,
payload: { itemId },
});
const reducer = (state, { type, payload }) => {
if (type === REMOVE) {
const { itemId } = payload;
return {
...state,
items: state.items.filter(({ id }) => id !== itemId),
};
}
return state;
};
//selectors
const selectItems = (state) => state.items;
const createSelectItemById = (itemId) =>
createSelector([selectItems], (items) =>
items.find(({ id }) => id === itemId)
);
//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 Item = React.memo(function Item({ itemId }) {
console.log('creating jsx for item:', itemId);
const selectItemById = React.useMemo(
() => createSelectItemById(itemId),
[itemId]
);
const item = useSelector(selectItemById);
const dispatch = useDispatch();
return (
<li>
<pre>{JSON.stringify(item)}</pre>
<button onClick={() => dispatch(remove(itemId))}>
remove
</button>
</li>
);
});
const App = () => {
const items = useSelector(selectItems);
return (
<ul>
{items.map(({ id }) => (
<Item key={id} itemId={id} />
))}
</ul>
);
};
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>
<div id="root"></div>
如果这对您不起作用,因为所有项目都已重新创建,那么您可以在 createSelectItemById 中构建内存:
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;
const initialState = {
items: JSON.stringify([
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 3, name: 'three' },
]),
};
//action types
const REMOVE = 'REMOVE';
//action creators
const remove = (itemId) => ({
type: REMOVE,
payload: { itemId },
});
const reducer = (state, { type, payload }) => {
if (type === REMOVE) {
const { itemId } = payload;
return {
...state,
items: JSON.stringify(
JSON.parse(state.items).filter(
({ id }) => id !== itemId
)
),
};
}
return state;
};
//selectors
// re creating items every time
const selectItems = (state) => JSON.parse(state.items);
const createSelectItemById = (itemId) => {
//comparing last to current and returning last
// if items are the same
let lastItem = {};
return createSelector([selectItems], (items) => {
const currentItem = items.find(
({ id }) => id === itemId
);
//only need to compare name, you may need more comparisons
if (currentItem.name === lastItem.name) {
return lastItem;
}
lastItem = currentItem;
return currentItem;
});
};
//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 Item = React.memo(function Item({ itemId }) {
console.log('creating jsx for item:', itemId);
const selectItemById = React.useMemo(
() => createSelectItemById(itemId),
[itemId]
);
const item = useSelector(selectItemById);
const dispatch = useDispatch();
return (
<li>
<pre>{JSON.stringify(item)}</pre>
<button onClick={() => dispatch(remove(itemId))}>
remove
</button>
</li>
);
});
const App = () => {
const items = useSelector(selectItems);
return (
<ul>
{items.map(({ id }) => (
<Item key={id} itemId={id} />
))}
</ul>
);
};
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>
<div id="root"></div>
推荐阅读
- openshift - 仅更新 OKD 下的 kubernetes ( OpenShift )
- micronaut - Micronaut Bean 初始化顺序
- flutter - Flare/Flutter:如何在开始之前重置动画的进度?
- django - 'QuerySet' 对象在使用 timesince 时没有属性'year'
- git - Github 如何存储提交消息?
- c# - 如何捕获 DataReceivedEventHandler 引发的异常?
- karate - 空手道新手:无法从带有连字符的响应中获取值
- javascript - JustGage 用 jquery 刷新
- python - 如何将inkml文件转换为图像格式
- spring - 如何避免 XML Spring bean 中的样板属性?