React - 多个组件确实更新了 - 为什么?

React - Multiple components did update - why?

作为 React 的新手,我正在尝试了解生命周期挂钩,这种情况是 ComponentDidUpdate()

所以我有一个位置 class 组件 - 它是一个简单的表单,带有供用户输入位置的输入。我的 handleSubmit 事件阻止了新的页面请求,而是更新了这个高阶组件中的状态。

在 location 中,我有一个天气 class 组件,它接收新位置作为 prop,用位置更新它自己的状态,并在 componentDidUpdate() 上触发一个 api 请求,然后它也设置它的解决获取后,说明该位置的天气状况。然后它将状态传递给 jsx div 这样用户就可以看到天气状况。

为此,我可以看到三个 componentDidUpdate() 事件从天气组件输出到控制台 - 我只是在猜测是什么导致了它们。
第一次从更高位置组件接收到新的位置道具时?
第二个将它自己的状态设置到新位置?
第三次,当获取被解析并设置天气状态时?
或者也许一个是当它用天气状况更新 div 时。

你能告诉我这是怎么回事吗,它真的会帮助我构建一个更好的应用程序并调试它。

谢谢,

菲尔

本地组件:

import React, { Component } from "react";
import './local.css';
import Earth from './Earth';
import { Weather } from '../spies/weather/Spy';

class Local extends Component {

    constructor() {
        super()
        this.state = {
            location:''
        }
    }

    handleSubmit = e => {
        e.preventDefault();
        if(e.target.elements.localInput.value) this.setState({ location: e.target.elements.localInput.value })        
        else return;
    }

    render() {
        return (
            <React.Fragment>
                <div id="local" >
                    <form className="appBorder" onSubmit={this.handleSubmit}>
                        <input id="localInput" className="appInput appBorder" type="text" placeholder="Enter the location">
                        </input>
                        <button className="appButton">
                            <Earth className="earth"/>
                        </button>
                    </form>
                </div>
                <Weather location={this.state.location}/>
            </React.Fragment>
        )
    }
}

export default Local;

天气组件(别名间谍):

import React, { Component } from "react";
import './weather.css';
import '../spy.css';
import { getWeather } from './api';

class Spy extends Component {

    constructor() {
        super()
        this.state = {
            location: null,
            weatherData: null,
            error: '',
         };
    }

    componentDidUpdate(prevProps, prevState) {
        console.log("update");
        if (prevProps.location !== this.props.location) {
            this.setState({location: this.props.location},()=>{
                getWeather(this.state.location)
                    .then(data => {
                        console.log("api request resolved");
                        this.setState({ weatherData: data });
                    })
                    .catch(error => 
                        this.setState({ error: error.message }));
            }
        )}       
        else return;
    }

    render() {
        return (
            <div id="spyWeather" className="appBorder spy">
                <h3 className="spyName">Weather for {this.state.location}</h3> 
                <p id="weatherDesc" className="spyData">Conditions: {this.state.weatherData ? this.state.weatherData.current.weather_descriptions : ""}</p> 
                <p id="weatherTemp" className="spyData">Temperature: {this.state.weatherData ? this.state.weatherData.current.temperature : ""} &deg;C</p>
                <p id="weatherHumid" className="spyData">Humidity: {this.state.weatherData ? this.state.weatherData.current.humidity : ""} %</p>
                <p id="weatherPrecip" className="spyData">Precipitation: {this.state.weatherData ? this.state.weatherData.current.precip : ""} mm</p>
            </div>
        )
    }
}

export { Spy as Weather };

控制台(2 次更新,api 触发然后另一个更新):-

[HMR] Waiting for update signal from WDS...
2 Spy.js:18 update
Spy.js:23 api request resolved
Spy.js:18 update

给定

componentDidUpdate(prevProps, prevState) {
    console.log("update");
    if (prevProps.location !== this.props.location) {
        this.setState({location: this.props.location},()=>{
            getWeather(this.state.location)
                .then(data => {
                    console.log("api request resolved");
                    this.setState({ weatherData: data });
                })
                .catch(error => 
                    this.setState({ error: error.message }));
        }
    )}       
    else return;
}

和日志

2 Spy.js:18 update
Spy.js:23 api request resolved
Spy.js:18 update

是的,我看到三个 renders/rerenders。

  1. 第一个“更新”是从 props.locationnull 变为 一些 值。条件测试 prevProps.location !== this.props.location 解析 true,因此采用快乐路径并使用位置更新状态。

  2. 第二次“更新”现在是因为状态更新为 location。 在上一次状态更新的同时,调用了 setState 回调并获取了天气信息。在承诺链的快乐路径中是日志“api 请求已解决”和另一个 setState.

  3. 第三次“更新”再次来自状态更新,这次是weatherData

如你所知,在本地状态中存储道具是反应中的反模式,只需发出你的副作用,比如在道具改变时获取天气。也不建议在 setState 回调中链接状态更新,因为每个嵌套状态更新都会延迟 that 更新 1(或更多)渲染周期,并且会使调试更加困难。最好在 componentDidUpdate 中简单地处理它们。也不需要“void”return,因为所有 JS 函数都有一个隐式 return,不需要 return 任何 actual值。

componentDidUpdate(prevProps, prevState) {
  console.log("update");
  if (prevProps.location !== this.props.location) {
    if (location) {
      // location prop changed and is truthy, get weather
      getWeather(this.state.location)
        .then(data => {
          console.log("api request resolved");
          this.setState({ weatherData: data });
        })
        .catch(error => this.setState({ error: error.message }));
    }     
  }
}

这应该刻画出那些最初“浪费的”渲染周期之一,日志现在应该是

Spy.js:18 update
Spy.js:23 api request resolved
Spy.js:18 update