[英]react component displaying in first render but not in the following re-render
I have a react component that consists in an alert to which I am passing 2 props, (however these 2 props rarely change within this component)我有一个反应组件,它包含在我传递 2 个道具的警报中,(但是这 2 个道具在这个组件中很少改变)
const AlertPopUp = ({ severity, errors }) => {
const [show, setShow] = useState(true)
console.log('show value of show state: ', show)
useEffect(() => {
console.log('gets here')
const timeId = setTimeout(() => {
// After 5 seconds set the show value to false
setShow(false)
}, 5000)
return () => {
clearTimeout(timeId)
}
});
console.log('errors ', errors)
if (!errors) return null
if (show)
return (
<>
<Alert severity={severity}>
{errors}
</Alert>
</>
)
}
In the first render, the Alert
shows up and after the expected 5 seconds the component disappears.在第一次渲染中,
Alert
出现,在预期的 5 秒后组件消失。
In the re-render, the Alert
does not show up anymore, and from my debugging I assume it has to do to with the line console.log('show value of show state: ', show)
which displays false in the re-render.在重新渲染中,
Alert
不再显示,并且从我的调试中我认为它与行console.log('show value of show state: ', show)
,它在重新显示为 false使成为。
If I do a setShow(true)
I run into an infinite loop of re-renders.如果我执行
setShow(true)
,我会遇到重新渲染的无限循环。
If I use a useRef
to avoid the useState
infinite loop the component doesn't re-render and therefore the Alert
never displays.如果我使用
useRef
来避免useState
无限循环,则组件不会重新渲染,因此Alert
永远不会显示。
If I try to set a key to the component key=useId()
/ pass a counter, the state is still set to false whenever the parent component rerenders, looking like the component doesn't destroy and create again.如果我尝试为组件
key=useId()
设置一个键/传递一个计数器,那么每当父组件重新渲染时,state 仍然设置为 false,看起来组件不会再次破坏和创建。
Please forgive me if I made any of my assumptions wrongly as I am far from being a react expert.如果我做出任何错误的假设,请原谅我,因为我远不是反应专家。
Could please anyone help me find a solution so that the Alert
displays in every render of the alert component and disappears after the 5 seconds?谁能帮我找到一个解决方案,让
Alert
显示在警报组件的每次渲染中并在 5 秒后消失?
发生这种情况是因为您从未将show
设置回true
One solution is to use a different key every time you want to show the component again .一种解决方案是每次想要再次显示组件时使用不同的键。 This way when you pass a different key when you want the alert to reappear, its old instance will be destroyed, and it will be like rendering that component from the beginning;
这样,当您希望警报重新出现时传递不同的键时,其旧实例将被销毁,并且就像从头渲染该组件一样; so the
show
flag won't be false
anymore, which prevents it from appearing in your original case.所以
show
标志将不再是false
的,这会阻止它出现在你原来的情况下。 Here is example:这是示例:
let Alert = (props) => {
let [show, setShow] = React.useState(true);
React.useEffect(() => {
setTimeout(() => {
setShow(false);
}, 5000);
}, []); // note empty array also
if (!show) return null;
return <div>alert</div>;
};
export default function App() {
let [id, setId] = React.useState(0);
return (
<div onClick={() => setId(id + 1)}>
Click to show alert
<Alert key={id} />
</div>
);
}
I think I understand the problem with your code.我想我理解你的代码的问题。 Let me explain, on initial render, show is true, so your alert component renders.
让我解释一下,在初始渲染时,显示为真,因此您的警报组件会渲染。 But you also set show to false .Now when your props change , which causes a re-render but because show is still false , your alert component doesn't re-render.
但是您也将 show 设置为 false 。现在,当您的道具更改时,这会导致重新渲染,但由于 show 仍然是 false ,您的警报组件不会重新渲染。 So try this;
所以试试这个; move your setTimeOut where you set setshow to false outside the useEffect ( it's still going to run ) and then inside your useEffect , set show to true and also pass your props (errors and severity) into the array that useEffect takes as second argument so that when those props change , your useEffect will run setting show to true which will cause ur component to re-render and show being true , your alert renders .
将您的 setTimeOut 设置为 false 在 useEffect 之外(它仍将运行),然后在您的 useEffect 内部,将 show 设置为 true 并将您的道具(错误和严重性)传递到 useEffect 作为第二个参数的数组中,以便当这些道具发生变化时,您的 useEffect 将运行设置 show 为 true ,这将导致您的组件重新渲染并显示为 true ,您的警报会渲染。
From what I can understand of your post, the AlertPopUp
component works without issue upon the initial mount and render.根据我对您帖子的了解,
AlertPopUp
组件在初始安装和渲染时可以正常工作。 The problem is then how to retrigger the component to render the Alert
.那么问题是如何重新触发组件以呈现
Alert
。
I suggest lifting state up and making the AlertPopUp
a fully controlled component that also consumes an onClose
callback to handle updating the show
state.我建议提升 state 并使
AlertPopUp
成为一个完全受控的组件,该组件还使用onClose
回调来处理更新show
state。
Example:例子:
const AlertPopUp = ({ show, severity = "error", errors, onClose }) => {
useEffect(() => {
const timeId = setTimeout(() => {
// After 5 seconds set the show value to false
onClose();
}, 5000);
return () => {
clearTimeout(timeId);
};
});
if (!errors) return null;
if (show) {
return (
<Alert onClose={onClose} severity={severity}>
{errors}
</Alert>
);
}
};
Usage:用法:
const [show, setShow] = useState(false);
const closeHandler = () => setShow(false);
return (
...
{/* Just an example to trigger the alert to display */}
<button type="button" onClick={() => setShow(true)}>
Show Alert
</button>
<AlertPopUp
{...{ errors, show, onClose: closeHandler }}
/>
...
);
Now when either the timeout expires or the user manually dismisses the alert the onClose
callback is called and will update the show
state in the ancestor to false
and hide the Alert
component.现在,当超时到期或用户手动解除警报时,将调用
onClose
回调并将祖先中的show
state 更新为false
并隐藏Alert
组件。
The issue essentially was:问题本质上是:
severity
here)severity
)show
state was not getting set to true
whenever props changeshow
state 未设置为true
Here, I have simulated the prop change behaviour by a counter and therefore after every 10 sec, alert would pop-up.在这里,我通过计数器模拟了道具更改行为,因此每 10 秒后会弹出警报。
Solution:解决方案:
severity
in deps arrayseverity
放在 deps 数组中useEffect
will trigger and then set show as true
.useEffect
都会触发,然后将 show 设置为true
。import { useRef, useEffect, useState } from "react";
// Dummy Alert Component
const Alert = ({ severity, children }) => {
alert(children);
return null;
};
// Reusable AlertPopUp component with same props
const AlertPopUp = ({ severity, errors }) => {
const [show, setShow] = useState(true);
const timeId = useRef();
useEffect(() => {
setShow(true);
timeId.current = setTimeout(() => {
// After 5 seconds set the show value to false
setShow(false);
}, 5000);
return () => {
clearTimeout(timeId.current);
};
}, [severity]);
if (!errors) return null;
if (show)
return (
<>
{" "}
<Alert severity={severity}>{errors}</Alert>{" "}
</>
);
};
// Component for simulating the behaviour
export default function App() {
const [count, setCount] = useState(0);
let counter = useRef();
useEffect(() => {
if (counter) {
counter.current = setTimeout(() => {
setCount(count + 1);
}, 10000);
}
return () => {
clearTimeout(counter.current);
};
}, [count]);
console.log("this is count", count);
return (
<div className="App">
<AlertPopUp severity={count} errors={`this is error: ${count}`} />
</div>
);
}
Link to Implementation in Sandbox:https://codesandbox.io/s/trusting-hertz-gjiexc?file=/src/App.js沙盒中的实现链接:https://codesandbox.io/s/trusting-hertz-gjiexc?file=/src/App.js
Here is my answer.这是我的答案。 You may need to use key if you want the component to re-render.
如果您希望组件重新渲染,您可能需要使用key 。 A link to the working code is here .
工作代码的链接在这里。
const AlertPopUp = ({ severity, errors }) => {
const [show, setShow] = useState(true);
useEffect(() => {
const timeId = setTimeout(() => {
// After 5 seconds set the show value to false
setShow(false);
}, 5000);
return () => {
clearTimeout(timeId);
};
});
return !errors ? null : show && <Alert severity={severity}>{errors}</Alert>;
};
Usage:用法:
<AlertPopUp key={key} severity={severity} errors={error}/>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.