13

I'm creating a chat app that works completely fine except for one issue: when I call a function that was passed to a child component, it uses the state variable's initial value, rather than its current value. I've included the code below and a snippit of the log that shows it using the wrong value. Why is it doing this? Is it related to LioWebRTC? Please advise.

Note: I added an extra button that calls the same function and the current state does print for that, so it seems like it's something to do with LioWebRTC? How is that even possible for it not to use the current state?

import React, { useState, useEffect } from 'react';
import { LioWebRTC } from 'react-liowebrtc';
import ChatBox from './ChatBox';


const ChatWrapper = props => {

  const [counter, setCounter] = useState(0)

  console.log("Correct Counter:", counter)

  useEffect( () => {
    setCounter(counter => counter +1 );
  } , [] );

  const addChat = (name, message, alert = false) => {};

  const join = (webrtc) => webrtc.joinRoom(props.roomID.toString());

  const handleCreatedPeer = (webrtc, peer) => {
    setCounter(counter => counter +1 );
  }

  const handlePeerData = (webrtc, type, payload, peer) => {
    console.log("Problem Counter: " , counter);
  }

  return (

    <div className='screenInterior'>

      <LioWebRTC
        options={{ debug: true,
                   dataOnly: true,
                   nick: "Blah" }}
        onReady={join}
        onCreatedPeer={handleCreatedPeer}
        onReceivedPeerData={handlePeerData}  >

        <ChatBox
          chatLog={null}
          onSend={(msg) => msg && addChat('Me', msg)}
        />

        <button onClick={handlePeerData}>Test</button>

      </LioWebRTC>

    </div>
  );
}

export default ChatWrapper;

enter image description here

Elliptica
  • 3,928
  • 3
  • 37
  • 68
  • Can you try adding key as counter on `LioWebRTC` – Yash Verma Mar 12 '22 at 07:07
  • @YashVerma When I add a key, as soon as another peer joins the room, that peer breaks with liowebrtc.js:174 ==> Uncaught TypeError: Cannot read properties of undefined (reading 'emit') at WebRTC. (liowebrtc.js:174:1) at WebRTC.prototype.emit (wildemitter.js:119:1) at Peer.send (peer.js:184:1) at Peer.onIceCandidate (peer.js:249:1) at PeerConnection.prototype.emit (wildemitter.js:119:1) at PeerConnection._onIce (rtcpeerconnection.js:905:1 <== In an infinite render cycle – Elliptica Mar 12 '22 at 07:17

1 Answers1

23

Create a reference of the state and pass it in the callback.

const [counter, setCounter] = useState(0)
const stateRef = useRef();
stateRef.current = counter;

const handlePeerData = (webrtc, type, payload, peer) => {
  console.log("Counter: " , stateRef.current);
}

Don't forget to import { useRef } from react.

For more information React hooks: accessing up-to-date state from within a callback

Edit: If you want to use a custom package, there is react-useStateRef help you have a state with reference in less code.

Import

import useState from 'react-usestateref'

Using

const [counter, setCounter, counterRef] = useState(0);
Abdulmuhaymin
  • 905
  • 7
  • 16
  • 1
    That seems to work! I don't really understand why though. I thought the only real difference between useState and useRef was that useRef doesn't cause a rerender when it updates? Also, why does counter work in a button callback but not the other callback? I've never had a state variable not work properly in a callback before. Is there some condition this happens under that useRef must be used for? – Elliptica Mar 12 '22 at 07:22
  • 1
    @Elliptica I'm not sure if I can explain it clearly, so I advise you to read this answer https://stackoverflow.com/a/62453660/11250643. – Abdulmuhaymin Mar 12 '22 at 07:32
  • Also, I don't know if you want to update your answer, but it should be fine to use ref directly, right? What I mean is, there's no advantage to having the ref point to a state variable if the variable is never being updated only the ref is. Isn't it better just to do `const stateRef = useRef(0)` directly and get rid of counter entirely? – Elliptica Mar 12 '22 at 07:39
  • 2
    @Elliptica The idea of using state is to rerender every time the state update (as you mention in your first comment), if you don't want to rerender, then you can use useRef directly without using state and ref point to the state. – Abdulmuhaymin Mar 12 '22 at 07:44