英文:
Why my state is empty when I try to access it from a function?
问题
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
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 even when I click to run hideDisplayFrame
after this, players
is not empty so why it is when expandVideoFrame
runs?.
英文:
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?.
答案1
得分: 1
这是因为你在其中使用了函数expandVideoFrame
来存储HTML。
因此,当它运行第一个useEffect
时,它会存储该函数而没有任何播放器。这就是为什么你总是得到一个空数组。
你必须只存储你想要的数据,并在组件的返回中渲染HTML。
英文:
It is because you store the HTML with the function expandVideoFrame
in it.
Thus when it runs the first useEffect
it stores the function without any players. That's why you always have an empty array.
You have to store only the data you want and render the HTML in the return of your component.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论