我在redux存储中有一个条目列表(从API返回的JSON对象)。这个数据目前是规范化的,所以它只是一个数组。它大约有10-30个对象,每个对象大约有10个属性。
目前,我们有一个顶级容器(使用react redux),它从存储区读取这个列表,在数组上映射,并呈现一个名为ListItem
的组件,它基本上需要对象中的3-4个字段来呈现UI。
我们现在对此没有任何性能问题。但是,我想知道为每个列表项设置一个redux容器组件是否有意义?我认为这将需要数据被规范化,并且我们需要将每个对象的唯一id传递给这个容器,然后容器可以从redux存储读取对象?
这个问题来自于Redux的样式指南- https://redux.js.org/style-guide/style-guide#connect-more-components-to-read-data-from-the-store
只是想了解在这个场景中使用redux的推荐方法是什么。
谢谢!
发布于 2020-07-09 08:51:10
,我想知道为每个列表项设置一个redux容器组件是否有意义?
除了可能有更好的性能,还有更好的代码重用。如果在列表中定义了项的逻辑,那么如何重用列表来呈现其他项?
下面是一个示例,其中Item是数据和编辑的组合,如果您要在列表中而不是项目中创建道具,那么所有项目的道具都会被重新创建。
List也不能用作将id传递给Item组件的常规列表。
const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { useMemo } = React;
const { createSelector } = Reselect;
const { produce } = immer;
const initialState = {
people: {
data: {
1: { id: 1, name: 'Jon' },
2: { id: 2, name: 'Marie' },
},
edit: {},
},
places: {
data: {
1: { id: 1, name: 'Rome' },
2: { id: 2, name: 'Paris' },
},
edit: {},
},
};
//action types
const SET_EDIT = 'SET_EDIT';
const CANCEL_EDIT = 'CANCEL_EDIT';
const SAVE = 'SAVE';
const CHANGE_TEXT = 'CHANGE_TEXT';
//action creators
const setEdit = (dataType, id) => ({
type: SET_EDIT,
payload: { dataType, id },
});
const cancelEdit = (dataType, id) => ({
type: CANCEL_EDIT,
payload: { dataType, id },
});
const save = (dataType, item) => ({
type: SAVE,
payload: { dataType, item },
});
const changeText = (dataType, id, field, value) => ({
type: CHANGE_TEXT,
payload: { dataType, id, field, value },
});
const reducer = (state, { type, payload }) => {
if (type === SET_EDIT) {
const { dataType, id } = payload;
return produce(state, (draft) => {
draft[dataType].edit[id] = draft[dataType].data[id];
});
}
if (type === CANCEL_EDIT) {
const { dataType, id } = payload;
return produce(state, (draft) => {
delete draft[dataType].edit[id];
});
}
if (type === CHANGE_TEXT) {
const { dataType, id, field, value } = payload;
return produce(state, (draft) => {
draft[dataType].edit[id][field] = value;
});
}
if (type === SAVE) {
const { dataType, item } = payload;
return produce(state, (draft) => {
const newItem = { ...item };
delete newItem.edit;
draft[dataType].data[item.id] = newItem;
delete draft[dataType].edit[item.id];
});
}
return state;
};
//selectors
const createSelectData = (dataType) => (state) =>
state[dataType];
const createSelectDataList = (dataType) =>
createSelector([createSelectData(dataType)], (result) =>
Object.values(result.data)
);
const createSelectDataById = (dataType, itemId) =>
createSelector(
[createSelectData(dataType)],
(dataResult) => dataResult.data[itemId]
);
const createSelectEditById = (dataType, itemId) =>
createSelector(
[createSelectData(dataType)],
(dataResult) => (dataResult.edit || {})[itemId]
);
const createSelectItemById = (dataType, itemId) =>
createSelector(
[
createSelectDataById(dataType, itemId),
createSelectEditById(dataType, itemId),
],
(item, edit) => ({
...item,
...edit,
edit: Boolean(edit),
})
);
//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 = ({ item, dataType }) => {
const dispatch = useDispatch();
return (
<li>
{item.edit ? (
<React.Fragment>
<input
type="text"
value={item.name}
onChange={(e) =>
dispatch(
changeText(
dataType,
item.id,
'name',
e.target.value
)
)
}
/>
<button
onClick={() =>
dispatch(cancelEdit(dataType, item.id))
}
>
cancel
</button>
<button
onClick={() => dispatch(save(dataType, item))}
>
save
</button>
</React.Fragment>
) : (
<React.Fragment>
{item.name}
<button
onClick={() =>
dispatch(setEdit(dataType, item.id))
}
>
edit
</button>
</React.Fragment>
)}
</li>
);
};
const createItem = (dataType) =>
React.memo(function ItemContainer({ id }) {
const selectItem = useMemo(
() => createSelectItemById(dataType, id),
[id]
);
const item = useSelector(selectItem);
return <Item item={item} dataType={dataType} />;
});
const Person = createItem('people');
const Location = createItem('places');
const List = React.memo(function List({ items, Item }) {
return (
<ul>
{items.map(({ id }) => (
<Item key={id} id={id} />
))}
</ul>
);
});
const App = () => {
const [selectPeople, selectPlaces] = useMemo(
() => [
createSelectDataList('people'),
createSelectDataList('places'),
],
[]
);
const people = useSelector(selectPeople);
const places = useSelector(selectPlaces);
return (
<div>
<List items={people} Item={Person} />
<List items={places} Item={Location} />
</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>
如果您的应用程序具有重复的逻辑,您可能想要将组件拆分到容器和表示中(容器也称为redux中的连接组件)。您可以重新使用容器,但可以更改表示。
https://stackoverflow.com/questions/62807725
复制相似问题