首页 > 解决方案 > React - 多个组件确实更新了 - 为什么?

问题描述

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

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

在位置内,我有一个天气类组件,它接收新位置作为道具,用位置更新自己的状态,并在 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;

Weather 组件(别名 Spy):

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

标签: reactjsreact-lifecycle

解决方案


给定

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

是的,我看到三个渲染/重新渲染。

  1. 第一个“更新”是从何时props.location更改null某个值。条件测试prevProps.location !== this.props.location解决true了,因此采用快乐路径并使用位置更新状态。

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

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

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

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

推荐阅读