显示错误: 在React中重新渲染次数过多

huangapple go评论72阅读模式
英文:

Displaying Error: Too many re-renders in React

问题

这是我编写的代码,用于根据用户输入更改背景视频。背景根据用户输入的地点的天气而变化。

import React, { Fragment, useState, useEffect } from "react";
import cloudy from "../assets/cloudy.mp4";
import sunny from "../assets/sunny.mp4";
import rainy from "../assets/rainy.mp4";
import winter from "../assets/winter.mp4";

const weathers = [cloudy, sunny, rainy, winter];

const Background = (props) => {
  const [weather, setWeather] = useState(weathers[1]);

  const temp = props.info.current.temp_c;
  const rain = props.info.current.precip_mm;

  if (rain > 2.5) setWeather(weathers[2]);
  else if (temp < 8) setWeather(weathers[3]);

  return (
    <Fragment>
      <video autoPlay loop muted className="back-video">
        <source src={weather} type="video/mp4" />
      </video>
    </Fragment>
  );
}

这是我在App组件中的返回方式:

return (
  <Fragment>
    <div className="container">
      <h1>Weather App</h1>
      <Input newLocation={locationHandler} />
      <article>{content}</article>
    </div>
    <article>{background}</article>
  </Fragment>
)

我尝试使用UseEffect来解决重新渲染错误,但在这种情况下,它根本不会改变背景。

useEffect(() => {
  if (rain > 2.5) setWeather(weathers[2]);
  else if (temp < 8) setWeather(weathers[3]);
}, [weather, temp, rain])

或者只将weather作为依赖项。

这是我的App组件:

import React, { useState, useEffect, useCallback, Fragment } from "react";
import Background from "./components/Background";
import Weather from "./components/Weather";
import Input from "./UI/Input";

function App() {
  // ...其他代码

  return (
    <Fragment>
      <div className="container">
        <h1>Weather App</h1>
        <Input newLoc={locationHandler} />
        <article>{content}</article>
      </div>
      <article>{background}</article>
    </Fragment>
  );
}

export default App;
英文:

This is the code that I have written to change the background video based on the user input. The background has a video based on the weather of the location the user enters.

import React, { Fragment, useState, useEffect } from &quot;react&quot;;
import cloudy from &quot;../assets/cloudy.mp4&quot;;
import sunny from &quot;../assets/sunny.mp4&quot;;
import rainy from &quot;../assets/rainy.mp4&quot;;
import winter from &quot;../assets/winter.mp4&quot;;

const weathers = [cloudy, sunny, rainy, winter];

const Background = (props) =&gt; {
const [weather, setWeather] = useState(weathers[1]);

const temp = props.info.current.temp_c;
const rain = props.info.current.precip_mm;

if (rain &gt; 2.5) setWeather(weathers[2]);
else if (temp &lt; 8) setWeather(weathers[3]);

return (
  &lt;Fragment&gt;
    &lt;video autoPlay loop muted className=&quot;back-video&quot;&gt;
      &lt;source src={weather} type=&quot;video/mp4&quot; /&gt;
    &lt;/video&gt;
  &lt;/Fragment&gt;
  );
}

Here's how I return in the App component :

return (
&lt;Fragment&gt;
  &lt;div className=&quot;container&quot;&gt;
    &lt;h1&gt;Weather App&lt;/h1&gt;
    &lt;Input newLocation={locationHandler} /&gt;
    &lt;article&gt;{content}&lt;/article&gt;
  &lt;/div&gt;
  &lt;article&gt;{background}&lt;/article&gt;
&lt;/Fragment&gt;
)

I tried using UseEffect since I have a re-rendering error but in that case it doesn't change the background at all

useEffect(() =&gt; {
 if (rain &gt; 2.5) setWeather(weathers[2]);
 else if (temp &lt; 8) setWeather(weathers[3]);
}, [weather, temp, rain])

or only weather as a dependency.

Edit: My App component

import React, { useState, useEffect, useCallback, Fragment } from 
&quot;react&quot;;
import Background from &quot;./components/Background&quot;;
import Weather from &quot;./components/Weather&quot;;
import Input from &quot;./UI/Input&quot;;

function App() {
  const [weather, setWeather] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [location, newLocation] = useState(&quot;New Delhi&quot;);

  const locationHandler = (place) =&gt; {
    newLocation(place);
  };

  const fetchweatherHandler = useCallback(async () =&gt; {
    setIsLoading(true);
    setError(null);
  try {
  //console.log(location);
  const response = await fetch(
    `http://api.weatherapi.com/v1/current.json? 
 key={apiKey}&amp;q=${location}&amp;aqi=yes`
  );

  if (!response.ok) {
    throw new Error(&quot;Something went wrong!&quot;);
  }

  const data = await response.json();
  //console.log(data);
  setWeather(data);
    } catch (error) {
  setError(error.message);
    }
setIsLoading(false);
  }, [location]);

  useEffect(() =&gt; {
fetchweatherHandler();
  }, [fetchweatherHandler]);

  let content = &lt;p&gt;Found no weather.&lt;/p&gt;;
  let background = &lt;p&gt;No Background&lt;/p&gt;;

  if (weather &amp;&amp; Object.keys(weather).length &gt; 0) {
    content = weather &amp;&amp; &lt;Weather info={weather} /&gt;;
    background = weather &amp;&amp; &lt;Background info={weather} /&gt;;
  }

  if (error) {
    content = &lt;p&gt;{error}&lt;/p&gt;;
  }

  if (isLoading) {
    content = &lt;p&gt;Loading...&lt;/p&gt;;
  }

  return (
&lt;Fragment&gt;
  &lt;div className=&quot;container&quot;&gt;
    &lt;h1&gt;Weather App&lt;/h1&gt;
    &lt;Input newLoc={locationHandler} /&gt;
    &lt;article&gt;{content}&lt;/article&gt;
  &lt;/div&gt;
  &lt;article&gt;{background}&lt;/article&gt;
&lt;/Fragment&gt;
  );
}

export default App;

答案1

得分: 2

UseEffect的工作原理如下。无论你在[]中传递了哪个状态,useEffect都会监听它的状态,每当状态发生变化时,它都会执行useEffect内部的代码。它还会在组件挂载时运行一次代码。

在你的情况下,你传递了[weather],并且在useEffect内部改变了setWeather,从而改变了weather。因此,useEffect再次运行,再次设置weather,导致无限循环和过多的重新渲染错误。

你可以像下面这样使用它,但它不会在每次weather发生变化时重新渲染。

useEffect(() => {
  if (rain > 2.5) setWeather(weathers[2]);
  else if (temp < 8) setWeather(weathers[3]);
}, [temp, rain])

查看有关"React函数组件中组件生命周期"的主题以了解更多信息。

英文:

So UseEffect works like this. Which ever state you are passing in the [], useEffect will listen to its state and whenever the state changes it executes the code inside the useEffect. It also runs the code once the component mounts.

In your case, you are passing [weather] and inside the useEffect you are changing the setWeather which changes weather. So useEffect runs again which set's weather again and it goes on causing an infinite loop and causing too many rerender error.

You can use it like the below but it won't rerender every time weather changes.

useEffect(() =&gt; {
 if (rain &gt; 2.5) setWeather(weathers[2]);
 else if (temp &lt; 8) setWeather(weathers[3]);
}, [temp, rain])

checkout the topic lifecycle of components in react functional components to know more about this

huangapple
  • 本文由 发表于 2023年2月10日 12:30:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/75406960.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定