React 组件未检测到 redux 状态的变化

时间:2021-01-18 07:51:19

标签: reactjs redux state

我在做一个聊天服务。

enter image description here

一切正常,期待以下功能:

  • 当收到新消息时,我希望更新聊天室列表(在左侧)。新消息的聊天室应该移动到第一个位置。但是,该组件不会自动检测更改。我需要点击列表才能更新。

为此,我尝试了以下减速器:

case NEW_CHAT_MESSAGE:
      const setRoomFirst = (array, roomId) => {
        const chatIndex = array.findIndex((arr) => arr._id === roomId);
        array.push(...array.splice(0, chatIndex));
        return { arr: array, hasBeenUpdated: chatIndex !== -1 };
      };

      const rooms = state.rooms;

      // move room to first position
      const updatedRooms = setRoomFirst(rooms, action.payload.message.chatRoom);

      if (updatedRooms.hasBeenUpdated) {
        // will always be the first room
        updatedRooms.arr[0].messages.push(action.payload.message);
      }

      return { ...state, rooms: updatedRooms.arr };
    default:
      return state;
  }

管理聊天状态的组件:

function Chat() {
  const classes = useStyle();
  const dispatch = useDispatch();

  const callback = useCallback(
    (rooms) => {
      console.log("dispatch");
      dispatch(newChatMessage(rooms));
    },
    [dispatch]
  );

  const { roomId } = useParams();
  const rooms = useSelector(ChatRoomsSelector);

  const [selectedRoom, setSelectedRoom] = useState(
    roomId || (rooms.length ? rooms[0]._id : null)
  );

  const oldRoomsValue = usePrevious(rooms);

  useEffect(() => {
    console.log("rooms has changed", rooms);
    if (!selectedRoom) {
      setSelectedRoom(roomId || (rooms.length ? rooms[0]._id : null));
    }
    if (oldRoomsValue !== rooms) {
      console.log("a change in rooms has been detected");
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [rooms]);

  const onSelect = useCallback((roomId) => {
    setSelectedRoom(roomId);
  }, []);

  const current = rooms.filter((room) => room._id === selectedRoom)[0];

  return (
    <Container>
      <Typography variant="h1" className={classes.title}>
        Messagerie
      </Typography>
      <Grid container spacing={2}>
        <Grid item xs={4}>
          <ChatListContainer
            rooms={rooms}
            roomSelected={selectedRoom}
            onSelect={onSelect}
          />
        </Grid>
        <Grid item xs={8}>
          {current && <ChatContentContainer room={current} />}
        </Grid>
      </Grid>
    </Container>
  );
}

export default Chat;

预先感谢您的帮助! :)

2 个答案:

答案 0 :(得分:1)

您正在改变 redux 状态并且您没有表明您正在使用 immer,因此您不应该这样做:

case NEW_CHAT_MESSAGE:
  const {
    payload: { message },
  } = action;
  //find or create room
  const room = state.rooms.find(
    (room) => room._id === message.chatRoom
  ) || { _id: message.chatRoom, messages: [] };
  //add message to room
  const roomWithMessage = {
    ...room,
    messages: [...room.messages, message],
  };
  return {
    ...state,
    //set room as first in rooms
    rooms: [
      //room with message is the first
      roomWithMessage,
      //other rooms without the room with message
      ...state.rooms.filter(
        (room) => room._id !== roomWithMessage._id
      ),
    ],
  };

您的效果缺少依赖项,当您希望效果运行时这可能会导致一些问题,并且我发现了一些其他不一致的地方(请参阅代码中的注释):

function Chat() {
  const dispatch = useDispatch();

  // your code does not show how you need to use this
  //  if this is to dispatch a new chatmessage then the
  //  paremeter passed would not be rooms but message {chatRoom:roomid, ...other}
  // const callback = useCallback(
  //   (rooms) => {
  //     console.log('dispatch');
  //     dispatch(newChatMessage(rooms));
  //   },
  //   [dispatch]
  // );

  const { roomId } = useParams();
  const rooms = useSelector(ChatRoomsSelector);

  const [selectedRoom, setSelectedRoom] = useState(
    //use optional chaining for cleaner syntax:
    //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
    roomId || rooms?.[0]?._id
  );
  //use Array.prototype.find if you want to find one item
  const current = rooms.find(
    (room) => room._id === selectedRoom
  );

  useEffect(() => {
    //a console.log does not show what you want to do here
    //  the only thing you actually did is change selectedRoom
    //  when current room changes and no selectedRoom was provided
    setSelectedRoom(selectedRoom=>selectedRoom || current?._id);
  }, [current]);

  const onSelect = useCallback((roomId) => {
    setSelectedRoom(roomId);
  }, []);

  return 'jsx';
}

答案 1 :(得分:1)

非常感谢您的回答HMR! 如果有机会,我会给你一个拥抱。 ^^

只需要更正一件事:我需要枚举减速器中过滤器的结果以使其工作。

return {
        ...state,
        // set room as first in rooms
        rooms: [
          // room with message is the first
          roomWithMessage,
          // other rooms without the room with message
          ...state.rooms.filter((room) => room._id !== roomWithMessage._id),
        ],
      };