setState не работает в ответных хуках с контекстным API? - PullRequest
0 голосов
/ 21 сентября 2019

Я получаю сообщения через socket.io, и я прослушиваю входящие сообщения внутри функции useEffect hooks.

Проблема в том, что при обновлении сообщений оно не обновляется успешно.

export const GroupContext = React.createContext({});

function GroupProvider(props){

   const socket = useMemo(()=> io(url), [socket]);
   const [messages, setMessages] = useState({});

   const setMessage = (message, room) => {
        if (messages[room] !== undefined){
           setMessages({ [room]: [message, ...messages[room]] });
        }
        else{
           setMessages({ [room]: [message] }); // updating messages per room
        }

        console.log('room: ', room); // I get the value: roomid100
        console.log('message: ', message); // I get the message: Hi dear, How are you
   }


   useEffect(() => {

      console.log('messages: ', messages); // But here I always get the last messages came.

   },[messages]);

   return (
        <GroupContext.Provider value={{socket,  messages, setMessage}}>
            {props.children}
        </GroupContext.Provider>
    );

}


function Group(props){

   const { socket, messages, setMessage } = useContext(GroupContext);

   const receiveMessages = ()=> {
       socket.on('getmessage', data => {
           setMessage(data.message, data.room);
       });
   }

   useEffect(()=> {

        if(socket.connected === true){

            receiveMessages();

        }else{
            socket.connect();
            setTimeout(() => {

               receiveMessages();

            }, 2000);
        }

        return ()=> unmoun();

    }, []);


    // ....

}


function Main(){
  // ....
  return (
        <GroupProvider>
            <Group />
        </GroupProvider>
  );

}

Примечание. Этот же код работает и в компоненте класса без использования хуков.

1 Ответ

1 голос
/ 21 сентября 2019

В вашем примере у вас есть две вещи: 1) setTimeout делает закрытие для вашей переменной messages (и именно поэтому вы видите {}) 2) Когда вы используете setMessages({a: 1}), вы удаляете все остальные поля из messagesсостояние

Итак, ваш код будет выполняться в следующем порядке (код, в котором выделены проблемы):

function GroupProvider(props){

   const socket = useMemo(()=> io(url), [socket]);
   const [messages, setMessages] = useState({});

   const setMessage = (message, room) => {
        // message = Hi dear, How are you, room: roomid100
        // messages: {}
        if (messages[room] !== undefined){
           setMessages({ [room]: [message, ...messages[room]] });
        }
        else{
           // Problem 1: Here you remove all other changes from you state
           setMessages({ [room]: [message] }); // updating messages per room
        }
        // Problem 2: Here you have a closure to your `messages` variable, so that it will be {}

        console.log('room: ', room); // I get the value: roomid100
        console.log('message: ', message); // I get the message: Hi dear, How are you
        setTimeout(() => {
            console.log('messages: ', messages); // But here always I get empty {}
        }, 2000);
   }


   return (
        <GroupContext.Provider value={{socket,  messages, setMessage}}>
            {props.children}
        </GroupContext.Provider>
    );

}

Таким образом, вы можете исправить это с помощью useEffect, чтобы вы могли проверить свою переменнуюв правильном пути.И я предлагаю вам использовать setMessages как функцию, чтобы упростить ваш код:

function GroupProvider(props) {
    const socket = useMemo(() => io(url), [socket]);
    const [messages, setMessages] = useState({});

    const setMessage = (message, room) => {
        setMessages((messages) => {
            if (messages[room] !== undefined) {
                return { ...messages, [room]: [message, ...messages[room]] };
            }
            return { ...messages, [room]: [message] };
        });
    };

    // we will console log, when `messages` state changes
    useEffect(
        () => {
            console.log('messages: ', messages);
        },
        [messages]
    );

    return <GroupContext.Provider value={{ socket, messages, setMessage }}>{props.children}</GroupContext.Provider>;
}
...