【问题标题】:setState does not work in react hooks with context api?setState 在带有上下文 api 的反应钩子中不起作用?
【发布时间】:2019-09-21 07:19:43
【问题描述】:

我正在通过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>
  );

}

注意: 相同的代码在类组件中也能正常工作,无需使用钩子。

【问题讨论】:

  • 您可以与我们分享任何错误消息吗?
  • 没有错误消息,它只是无法更新消息,如您所见,我在单独的房间中将上一个消息与当前消息附加,因此它不会附加或更新消息。
  • 在您的 setmessage 中,更改 setMessages({ [room]: [message, ...messages[room]] });到 setMessages({ [ [message, ...messages[room]] }); } 看看它是否有效?
  • 或者改成setMessages("test")去调试看看有没有得到数据
  • setMessages({ [ [message, ...messages[room]] }); 这对于分隔每个房间的消息是不正确的。

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


【解决方案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>;
}

【讨论】:

  • 谢谢你的回答,正如你提到的Problem 1。但是这里else block 只是第一次执行,第二次messages[room] 不会是undefined,if block 将被执行并附加消息。
  • 尝试使用接收函数的setState:setMessages((messages) =&gt; { 我创建了一个与您的问题非常接近的代码框示例(但没有socket.io:codesandbox.io/s/youthful-khorana-w5pdr
  • 非常感谢您的代码解决了问题。我还从if block 中删除了...messages,如下所示:const setMessage = (message, room) =&gt; { setMessages(messages =&gt; { if (messages[room] !== undefined) return {[room]: [message, ...messages[room]] }; else return { ...messages, [room]: [message] }; }); }
猜你喜欢
  • 1970-01-01
  • 2019-04-19
  • 1970-01-01
  • 2020-09-24
  • 2021-04-10
  • 2021-06-03
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
相关资源
最近更新 更多