score:1

Accepted answer

You are mutating the redux state and you did not indicate you are using immer so that's something you should not do:

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
      ),
    ],
  };

Your effect has missing dependencies, this can cause some problem when you want the effect to run and I found some other inconsistancies (see comments in code):

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';
}

score:1

Thank you so much HMR for your answer ! I would give you a hug if I had a chance. ^^

Just one thing to correct : I needed to enumerate the result of filter in the reducer to make it work.

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),
        ],
      };

Related Query

More Query from same tag