React 的 useState 问题,太多的重新渲染

Problem with useState from React, too many re-render

我正在将 fullcalendar 库用于一个小的笔记项目 'to do',只是为了学习 React js(我是新手)。当我从 firebase 上的实时数据库中获取数据时,我尝试在日历中显示事件名称 (evento) 和日期 (fecha),但是当我使用 setName() 来放置数据并将其保存到数组中时name=[],然后通过我重新渲染之类的错误,我不知道如何解决....如果有人能帮助我,那就太好了!!

(抱歉我的英语不好)

谢谢

这是错误和代码:

enter image description here

    const [date, SetFecha] = useContext(Contexto)
    const [name, setName] = useState([])

    firebase.database().ref().on('value', (snapshot) => {
        let obj = snapshot.val()
        Object.values(obj.Notas).forEach(e => {
            setName([...name, { title: e.evento, date: e.fecha }])
        });
    })



    const DateClick = (arg) => {
        SetFecha(arg.dateStr)    
    }

    return (
        <FullCalendar
            plugins={[dayGridPlugin, listPlugin, interactionPlugin]}
            initialView="dayGridMonth"

            dateClick={DateClick}
            events={name} 
        />
    )

解决方案

 useEffect(() => {
        firebase.database().ref().on('value', (snapshot) => {
            let obj = snapshot.val()
            let arr = []
            Object.values(obj.Notas).forEach(e => {
                arr.push({ title: e.evento, date: e.fecha })
            });
            setName(arr)
        })
    }, []);

您想使用 useEffect 挂钩。尝试类似的东西:

  useEffect(() => {
    firebase.database().ref().on('value', (snapshot) => {
      const obj = snapshot.val();
      const arrData = [];
      Object.values(obj.Notas).forEach(e => 
        arrData.push({ title: e.evento, date: e.fecha }));
      setName(arrData);
    });
  }, firebase.database().ref().on('value'));

你在上面代码中显示的问题是,每次调用 setName() 时,组件都是 re-rendered (这是 React 返回的一个特殊函数,用于更改组件状态和 re-render).当它 re-renders 时,再次调用 setName(),从而导致 re-rendering 中的无限循环。

useEffect()不是re-render中的运行,而是运行只有在检测到数据发生变化时才会出现。在这种情况下,它会检查 firebase.database().ref().on('value') 是否已更改,如果是,运行 useEffect() 和 re-render.

中的代码

更多可以阅读here

顺便说一句,可以应用一个小的优化是使用局部变量来存储过程中的值。然后只调用 setName() 一次。因为如前所述,每次调用这个特殊函数时,它 可能会导致 re-render。所以你只想在完成所有中间过程后调用它一次。或者更好的是,以函数式风格进行:

  useEffect(() => {
    firebase.database().ref().on('value', (snapshot) => {
      setName(Object.values(snapshot.val().Notas).map(e => 
        { title: e.evento, date: e.fecha }));
    });
  }, firebase.database().ref().on('value'));

您有 2 个问题:

  1. firebase 在 react 组件中创建监听器。
  2. Array.prototype.forEach 中调用 setName;

我会告诉你循环在哪里。

  1. 组件已渲染;
  2. 组件创建状态 [name, setName];
  3. 组件调用 firebase.database().ref().on('value', callback);
  4. Callback 从 firebase 获取数据并通过 forEach 按键迭代;
  5. 在 forEach 的第一次迭代中调用 setName;
  6. setName 呼叫 re-render;
  7. 转到3步;

解法:

function OwnComponent(props) {
  const [date, setFecha] = useContext(Contexto);
  const [name, setName] = useState([]);
    
  const dateClick = React.useCallback((arg) => {
    setFecha(arg.dateStr);
  }, [setFecha]);
    
  React.useEffect(() => {
    const unsubscribe = firebase.database().ref(/* you can pass 'Notas' 
 here */).on('value', (snapshot) => {
      setName(Object.values(snapshot.val().Notas/* you can remove .Notas here */).map(
        ({ evento: title, fecha: date }) => ({ title, date })
      ));
    });

    return () => {
      unsubscribe();
    };
  }, []);
    
  return (
    <FullCalendar
      plugins={[dayGridPlugin, listPlugin, interactionPlugin]}
      initialView="dayGridMonth"
      dateClick={dateClick}
      events={name} 
    />
  );
}