Search code examples
reactjsuse-effectuse-statereact-functional-component

React functional component access state in useeffect


I've gote some react component like below. I can use "messages" in return, but if I try to access messages inside some function, or useEffect, as in example, I always become initial value. How can I solve it in functional component? Thanks

const Messages = () => {

   const { websocket } = useContext(WebsocketsContext);

   let [ messages, setMessages ] = useState([]);

   useEffect(() => {
      getMessages()
      .then(result => {
         setMessages(result);
      })
   }, []);

   useEffect(() => {
      if(websocket != null){
         websocket.onmessage = (msg) => {
            let wsData = JSON.parse(msg.data);

            if(wsData.message_type == 'Refresh'){
               console.log(messages)
            };
         };
      };
   }, [websocket]);

   return(
        <div>...</div>    
   );

};
export default Messages;

Solution

  • Looks like you have encountered a stale closure

    the useEffect with [websockets] in its dependency array will only ever "update" whenever the websocket reference/value changes. Whenever it does, the function will have created a "closure" around messages at that point in time. Thus, the value of messages will stay as is within that closure. If messages updates after websocket has been created, it will never update the value of "messages" within the onmessage callback function.

    To fix this, add "messages" to the dependency array. [websockets, messages]. This will ensure the useEffect callback always has the latest state of messages, and this the onmessage function will have the latest state of messages.

     useEffect(() => {
          if(websocket != null){
             websocket.onmessage = (msg) => {
                let wsData = JSON.parse(msg.data);
    
                if(wsData.message_type == 'Refresh'){
                   console.log(messages)
                };
             };
          };
       }, [websocket, messages]);