[英]How to stop re-rendering a whole list of items when only one item of the list is created or updated in ReactJs React-Redux?
我正在制作这个 web 应用程序,其中包含用户可以回答这些帖子的帖子。 我已经使用 React-Redux 来管理应用程序的 state。 每次我创建或更新特定帖子的答案时,属于该帖子的整个答案列表都会重新呈现,我想停止它并仅呈现新创建或更新的答案。 我对发表评论使用了完全相同的方式,并且效果很好。 评论不会重新呈现,但答案会。 我只是无法弄清楚这里有什么问题。 请参考下面的代码。
我也尝试使用React.memo()
,但它也不起作用!
回答渲染组件,
export function Answer() {
const classes = useStyles();
const dispatch = useDispatch();
const { postId } = useParams();
const postAnswers = useSelector(state => state.Answers);
const [answers, setAnswers] = React.useState(postAnswers.answers);
React.useEffect(() => {
if(postAnswers.status === 'idle') dispatch(fetchAnswers(postId));
}, [dispatch]);
React.useEffect(() => {
if(postAnswers.answers) handleAnswers(postAnswers.answers);
}, [postAnswers]);
const handleAnswers = (answers) => {
setAnswers(answers);
};
const AnswersList = answers ? answers.map(item => {
const displayContent = item.answerContent;
return(
<Grid item key={item.id}>
<Grid container direction="column">
<Grid item>
<Paper component="form" className={classes.root} elevation={0} variant="outlined" >
<div className={classes.input}>
<Typography>{displayContent}</Typography>
</div>
</Paper>
</Grid>
</Grid>
</Grid>
);
}): undefined;
return(
<Grid container direction="column" spacing={2}>
<Grid item>
<Divider/>
</Grid>
<Grid item>
<Grid container direction="column" alignItems="flex-start" justify="center" spacing={2}>
{AnswersList}
</Grid>
</Grid>
<Grid item>
<Divider/>
</Grid>
</Grid>
);
}
获取答案 redux 适用,
export const fetchAnswers = (postId) => (dispatch) => {
dispatch(answersLoading());
axios.get(baseUrl + `/answer_api/?postBelong=${postId}`)
.then(answers =>
dispatch(addAnswers(answers.data))
)
.catch(error => {
console.log(error);
dispatch(answersFailed(error));
});
}
发布答案,
export const postAnswer = (data) => (dispatch) => {
axios.post(baseUrl + `/answer_api/answer/create/`,
data
)
.then(response => {
console.log(response);
dispatch(fetchAnswers(postBelong)); //This is the way that I update answers state every time a new answer is created or updated
})
.catch(error => {
console.log(error);
});
}
任何帮助都会很棒。 谢谢!
添加项目后,您从 api 获取所有项目,以便在 state 中重新创建所有项目。 如果您为容器组件提供项目的 id 并让选择器将项目获取为 JSON 然后解析回 object 您可以记住它并防止重新渲染,但我认为重新渲染可能更好。
这是该项目的记忆 JSON 示例:
const { Provider, useDispatch, useSelector } = ReactRedux; const { createStore, applyMiddleware, compose } = Redux; const { createSelector } = Reselect; const fakeApi = (() => { const id = ((num) => () => ++num)(1); const items = [{ id: 1 }]; const addItem = () => Promise.resolve().then(() => items.push({ id: id(), }) ); const updateFirst = () => Promise.resolve().then(() => { items[0] = {...items[0], updated: id() }; }); const getItems = () => //this is what getting all the items from api // would do, it re creates all the items Promise.resolve(JSON.parse(JSON.stringify(items))); return { addItem, getItems, updateFirst, }; })(); const initialState = { items: [], }; //action types const GET_ITEMS_SUCCESS = 'GET_ITEMS_SUCCESS'; //action creators const getItemsSuccess = (items) => ({ type: GET_ITEMS_SUCCESS, payload: items, }); const getItems = () => (dispatch) => fakeApi.getItems().then((items) => dispatch(getItemsSuccess(items))); const update = () => (dispatch) => fakeApi.updateFirst().then(() => getItems()(dispatch)); const addItem = () => (dispatch) => fakeApi.addItem().then(() => getItems()(dispatch)); const reducer = (state, { type, payload }) => { if (type === GET_ITEMS_SUCCESS) { return {...state, items: payload }; } return state; }; //selectors const selectItems = (state) => state.items; const selectItemById = createSelector( [selectItems, (_, id) => id], (items, id) => items.find((item) => item.id === id) ); const createSelectItemAsJSON = (id) => createSelector( [(state) => selectItemById(state, id)], //return the item as primitive (string) (item) => JSON.stringify(item) ); const createSelectItemById = (id) => createSelector( [createSelectItemAsJSON(id)], //return the json item as object (item) => JSON.parse(item) ); //creating store with redux dev tools const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const store = createStore( reducer, initialState, composeEnhancers( applyMiddleware( ({ dispatch, getState }) => (next) => (action) => //simple thunk implementation typeof action === 'function'? action(dispatch, getState): next(action) ) ) ); const Item = React.memo(function Item({ item }) { const rendered = React.useRef(0); rendered.current++; return ( <li> rendered:{rendered.current} times, item:{' '} {JSON.stringify(item)} </li> ); }); const ItemContainer = ({ id }) => { const selectItem = React.useMemo( () => createSelectItemById(id), [id] ); const item = useSelector(selectItem); return <Item item={item} />; }; const ItemList = () => { const items = useSelector(selectItems); return ( <ul> {items.map(({ id }) => ( <ItemContainer key={id} id={id} /> ))} </ul> ); }; const App = () => { const dispatch = useDispatch(); React.useEffect(() => dispatch(getItems()), [dispatch]); return ( <div> <button onClick={() => dispatch(addItem())}> add item </button> <button onClick={() => dispatch(update())}> update first item </button> <ItemList /> </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> <div id="root"></div>
我刚刚发现导致上述问题的问题出在哪里。 在我的 state 管理系统中,有一个名为answers
的操作来处理 state 的帖子答案,如下所示。
import * as ActionTypes from '../ActionTypes';
export const Answers = (state = {
status: 'idle',
errMess: null,
answers: []
}, action) => {
switch(action.type) {
case ActionTypes.ADD_ANSWER_LIST:
return {...state, status: 'succeeded', errMess: null, answers: action.payload}
case ActionTypes.ANSWER_LIST_LOADING:
return {...state, status: 'loading', errMess: null, answers: []}
case ActionTypes.ANSWER_LIST_FAILED:
return {...state, status: 'failed', errMess: action.payload, answers: []}
default:
return state;
}
}
这里的问题是我在ANSWER_LIST_LOADING
和ANSWER_LIST_FAILED
案例中放入的空 arrays 。 每次动作创建者获取新数据时,它都会loading
state 并在那里得到一个空数组,导致整个答案列表被重新渲染和不必要地重新创建。 所以我改变了实现如下,它解决了这个问题。
export const Answers = (state = {
status: 'idle',
errMess: null,
answers: []
}, action) => {
switch(action.type) {
case ActionTypes.ADD_ANSWER_LIST:
return {...state, status: 'succeeded', errMess: null, answers: action.payload}
case ActionTypes.ANSWER_LIST_LOADING:
return {...state, status: 'loading', errMess: null, answers: [...state.answers]}
case ActionTypes.ANSWER_LIST_FAILED:
return {...state, status: 'failed', errMess: action.payload, answers: [...state.answers]}
default:
return state;
}
}
一直以来,问题都出现在我从未想过会出现的地方。 我什至没有在我的问题中提到这个action
。 但是你有 go。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.