setState在与上下文api的React Hook中不起作用吗?

时间:2019-09-21 07:19:43

标签: reactjs react-native socket.io react-hooks

我正在通过socket.io接收消息,并且正在useEffect挂钩函数中监听传入消息。

问题是当我更新消息时,它没有成功更新。

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 个答案:

答案 0 :(得分:1)

在您的示例中,您有两件事: 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>;
}