I have this room component for video chat, it has two states, players
to hold all elements, it is an array of nodes, each is a div, created when a user joins the room (this happens inside useEffect
) and videoInDisplayFrame
to hold the current element in display frame, when the user clicks on a specific player we set this one to videoInDisplayFrame
.
import { useState, useEffect, useRef } from "react";
import styles from "./Room.module.css";
export default function Room() {
const [players, setPlayers] = useState([]);
const [videoInDisplayFrame, setVideoInDisplayFrame] = useState(null);
const displayFrame_ref = useRef();
const streams__container_ref = useRef();
let expandVideoFrame = (e) => {
console.log("expandVideoFrame is running... players:",players,"videoInDisplayFrame:",videoInDisplayFrame);
displayFrame_ref.current.style.display = "block";
//if videoInDisplayFrame is not null
if (videoInDisplayFrame) {
// add videoInDisplayFrame to players
setPlayers((prev) => [...prev, videoInDisplayFrame]);
}
// find from players the one with the same id as e.currentTarget
const newVideoInDisplayFrame = players.find((element) =>
element.props.id === e.currentTarget.id;
);
// then if it exists, add it to videoInDisplayFrame
if (newVideoInDisplayFrame) {
setVideoInDisplayFrame(newVideoInDisplayFrame);
}
};
let hideDisplayFrame = () => {
console.log("hideDisplayFrame is running... players:",players,"videoInDisplayFrame:",videoInDisplayFrame);
displayFrame_ref.current.style.display = null;
// add videoInDisplayFrame to players
setPlayers([...players, videoInDisplayFrame]);
// set videoInDisplayFrame to null
setVideoInDisplayFrame(null);
};
useEffect(() => {
console.log("useEffect is running...");
// after component first mount
let uid = String(Math.floor(Math.random() * 10000));
// create JSX element
let player = (
<div
// ref={videoFrames_refs[videoFrames_refs.length]}
className={styles.video__container}
id={`user-container-${uid}`}
onClick={expandVideoFrame}
>
<div className={styles.video_player} id={`user-${uid}`}></div>
</div>
);
// add it to players
setPlayers((prev) => [...prev, player]);
}, []);
return (
<>
{console.log("This is a new render... players: ",players,"and videoInDisplayFrame: ",videoInDisplayFrame)}
<section
className={styles.stream__container}
>
<div
ref={displayFrame_ref}
id="stream__box"
className={styles.stream__box}
onClick={hideDisplayFrame}
>
{videoInDisplayFrame}
</div>
<div
ref={streams__container_ref}
className={styles.streams__container}
>
{/* display players inside JSX */}
{players.map((element, index) => (
<div key={index}>{element}</div>
))}
</div>
</section>
</>
);
}
when I mount the components for the first time and then click on the only player I have to display it, I can see that my players
state is an empty array, I expected it to contain the element so I can remove it and set it inside videoInDisplayFrame
because useEffect
should update it so the component rerenders and the state is updated before I click, I even added this button
<button
onClick={() => {
console.log("current state... players: ",players,"videoInDisplayFrame:",videoInDisplayFrame);
}}
>
current state
</button>
to be able to see the state at any moment and the output was confusing :

as you can see, even when I clicked on the button to see the state before expandVideoFrame
and after it and it is not empty event when I click to run hideDisplayFrame
after this, players
is not empty so why it is when expandVideoFrame
runs?.
Note:
inside expandVideoFrame
I tried this just for test:
setPlayers(prev => {
console.log("prev: ", prev)
return [];
})
and prev was not empty, it contains the node element, but I expect players
to have the same value as well, what am I missing? thank you for your time.