React-component 不会在 store 更改时重新渲染,既不会自动更新也不会强制更新
React-component is not re-rendered when the store is changed, neither automatically nor even by force update
此功能组件应显示一个排序列表,其中每个项目的复选框都会更改商店中的值。
由于某种原因,商店更改时未重新呈现。如果没有重新渲染器,它(和整个应用程序)的工作就会非常歪曲和半途而废。我怀疑这是因为商店对象保持不变,尽管有新内容。但我不明白如何解决它。我什至在复选框处理程序中插入了强制更新,但由于某种原因它也不起作用。
组件:
import React, { useState, useReducer } from 'react';
import { ReactSortable } from 'react-sortablejs';
import ListItem from '@mui/material/ListItem';
import Checkbox from '@mui/material/Checkbox';
import { connect } from 'react-redux';
import { setGameVisible, setGameInvisible } from '../store/actions/games';
interface IGamesListProps {
games: [];
setGameVisible: (id: string) => void;
setGameInvisible: (id: string) => void;
}
interface ItemType {
id: string;
name: string;
isVisible: boolean;
}
const GamesList: React.FunctionComponent<IGamesListProps> = ({games, setGameVisible, setGameInvisible}) => {
const [state, setState] = useState<ItemType[]>(games);
// eslint-disable-next-line
const [ignored, forceUpdate] = useReducer(x => x + 1, 0); // this way of force updating is taken from the official React documentation (but even it doesn't work!)
const onCheckboxChangeHandle = (id: string, isVisible: boolean) => {
isVisible ? setGameInvisible(id) : setGameVisible(id);
forceUpdate(); // doesn't work :(((
}
return (
<ReactSortable list={state} setList={setState} tag='ul'>
{state.map((item) => (
<ListItem
sx={{ maxWidth: '300px' }}
key={item.id}
secondaryAction={
<Checkbox
edge="end"
onChange={() => onCheckboxChangeHandle(item.id, item.isVisible)}
checked={item.isVisible}
/>
}
>
{item.name}
</ListItem>
))}
</ReactSortable>
);
};
export default connect(null, { setGameVisible, setGameInvisible })(GamesList);
减速器:
import { SET_GAMES, SET_GAME_VISIBLE, SET_GAME_INVISIBLE } from '../actions/games';
export const initialState = {
games: [],
};
export default function games(state = initialState, action) {
switch(action.type) {
case SET_GAMES: {
for(let obj of action.payload.games) {
obj.isVisible = true;
}
return {
...state,
games: action.payload.games,
};
}
case SET_GAME_VISIBLE: {
for(let obj of state.games) {
if (obj.id === action.payload.id) {
obj.isVisible = true;
};
}
return {
...state,
};
}
case SET_GAME_INVISIBLE: {
for(let obj of state.games) {
if (obj.id === action.payload.id) {
obj.isVisible = false;
};
}
return {
...state,
};
}
default:
return state;
}
}
感谢您的帮助!
注意:根据您提供的信息,我想到了这个问题,但我张贴在这里是因为它会是解释性的而且很长。
- 首先,您不会通过
mapStateToProps
将新游戏传递到状态更改的组件中,即使您这样做,useState
也不会为 non-first渲染。您必须使用 useEffect 并触发游戏的变化并在本地设置状态。
At this point you must find the inner state redundant and you can remove it and totally rely on the redux state.
const mapStateToProp = (state) => ({
games: state.games // you may need to change the path
})
connect(mapStateToProp, { setGameVisible, setGameInvisible })(GamesList);
- 其次,您制作的 reducer 会更改单个游戏项目,但不会更改游戏列表本身。因为它是嵌套的,默认情况下引用检查是作为严格相等引用 check-in redux
state === state
完成的。这可能不会引起问题,因为外部状态会顺便改变,但我认为值得一提。
for(let obj of action.payload.games) {
obj.isVisible = true; // mutating actions.payload.games[<item>]
}
return {
...state,
games: [...action.payload.games], // add immutability for re-redenr
};
// or use map
return {
...state,
games: action.payload.games.map(obj => ({...obj, isVisible:true})),
};
- 第三,你的
forceUpdate
确实会导致组件 re-render,你可以通过添加 console.log 来测试它,但它不会重绘整个子树你的组件包括 inner children 如果他们的道具没有改变那是因为性能问题。 React 尝试尽可能高效地更新。还有你的 key
优化层,如果项目的顺序和它们的 id 没有改变,它会阻止改变
此功能组件应显示一个排序列表,其中每个项目的复选框都会更改商店中的值。
由于某种原因,商店更改时未重新呈现。如果没有重新渲染器,它(和整个应用程序)的工作就会非常歪曲和半途而废。我怀疑这是因为商店对象保持不变,尽管有新内容。但我不明白如何解决它。我什至在复选框处理程序中插入了强制更新,但由于某种原因它也不起作用。
组件:
import React, { useState, useReducer } from 'react';
import { ReactSortable } from 'react-sortablejs';
import ListItem from '@mui/material/ListItem';
import Checkbox from '@mui/material/Checkbox';
import { connect } from 'react-redux';
import { setGameVisible, setGameInvisible } from '../store/actions/games';
interface IGamesListProps {
games: [];
setGameVisible: (id: string) => void;
setGameInvisible: (id: string) => void;
}
interface ItemType {
id: string;
name: string;
isVisible: boolean;
}
const GamesList: React.FunctionComponent<IGamesListProps> = ({games, setGameVisible, setGameInvisible}) => {
const [state, setState] = useState<ItemType[]>(games);
// eslint-disable-next-line
const [ignored, forceUpdate] = useReducer(x => x + 1, 0); // this way of force updating is taken from the official React documentation (but even it doesn't work!)
const onCheckboxChangeHandle = (id: string, isVisible: boolean) => {
isVisible ? setGameInvisible(id) : setGameVisible(id);
forceUpdate(); // doesn't work :(((
}
return (
<ReactSortable list={state} setList={setState} tag='ul'>
{state.map((item) => (
<ListItem
sx={{ maxWidth: '300px' }}
key={item.id}
secondaryAction={
<Checkbox
edge="end"
onChange={() => onCheckboxChangeHandle(item.id, item.isVisible)}
checked={item.isVisible}
/>
}
>
{item.name}
</ListItem>
))}
</ReactSortable>
);
};
export default connect(null, { setGameVisible, setGameInvisible })(GamesList);
减速器:
import { SET_GAMES, SET_GAME_VISIBLE, SET_GAME_INVISIBLE } from '../actions/games';
export const initialState = {
games: [],
};
export default function games(state = initialState, action) {
switch(action.type) {
case SET_GAMES: {
for(let obj of action.payload.games) {
obj.isVisible = true;
}
return {
...state,
games: action.payload.games,
};
}
case SET_GAME_VISIBLE: {
for(let obj of state.games) {
if (obj.id === action.payload.id) {
obj.isVisible = true;
};
}
return {
...state,
};
}
case SET_GAME_INVISIBLE: {
for(let obj of state.games) {
if (obj.id === action.payload.id) {
obj.isVisible = false;
};
}
return {
...state,
};
}
default:
return state;
}
}
感谢您的帮助!
注意:根据您提供的信息,我想到了这个问题,但我张贴在这里是因为它会是解释性的而且很长。
- 首先,您不会通过
mapStateToProps
将新游戏传递到状态更改的组件中,即使您这样做,useState
也不会为 non-first渲染。您必须使用 useEffect 并触发游戏的变化并在本地设置状态。
At this point you must find the inner state redundant and you can remove it and totally rely on the redux state.
const mapStateToProp = (state) => ({
games: state.games // you may need to change the path
})
connect(mapStateToProp, { setGameVisible, setGameInvisible })(GamesList);
- 其次,您制作的 reducer 会更改单个游戏项目,但不会更改游戏列表本身。因为它是嵌套的,默认情况下引用检查是作为严格相等引用 check-in redux
state === state
完成的。这可能不会引起问题,因为外部状态会顺便改变,但我认为值得一提。
for(let obj of action.payload.games) {
obj.isVisible = true; // mutating actions.payload.games[<item>]
}
return {
...state,
games: [...action.payload.games], // add immutability for re-redenr
};
// or use map
return {
...state,
games: action.payload.games.map(obj => ({...obj, isVisible:true})),
};
- 第三,你的
forceUpdate
确实会导致组件 re-render,你可以通过添加 console.log 来测试它,但它不会重绘整个子树你的组件包括 inner children 如果他们的道具没有改变那是因为性能问题。 React 尝试尽可能高效地更新。还有你的key
优化层,如果项目的顺序和它们的 id 没有改变,它会阻止改变