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 没有改变,它会阻止改变