简体   繁体   English

反应组件在第一次渲染中显示,但在以下重新渲染中不显示

[英]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 ,您的警报会渲染。

Issue问题

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

Solution解决方案

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组件。

编辑 react-component-displaying-in-first-render-but-not-in-the-following-re-render

The issue essentially was:问题本质上是:

  1. Alert was not getting triggered after change of props ( severity here)更改道具后未触发警报(此处为severity
  2. show state was not getting set to true whenever props change每当道具更改时, show 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:解决方案:

  1. put severity in deps arrayseverity放在 deps 数组中
  2. On every prop change 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM