【问题标题】:React component doesn't re-render when state changes状态更改时 React 组件不会重新渲染
【发布时间】:2019-02-27 09:06:34
【问题描述】:

我正在制作一个反应应用程序,用户可以在其中输入机场的标识符,该应用程序将从 Web API 获取相应的天气数据并将其显示在表格中(例如:CYOW --> http://avwx.rest/api/metar/CYOW )。我已经到了指定用户搜索的机场的 API 端点的“url”状态在他们输入值时正在更新的地步,但是当状态发生变化时组件不会重新呈现,因此我的表不是更新。我在这里做错了什么?对不起,糟糕的代码,这是我第一次尝试 React + JS。谢谢。

import React, { Component } from 'react';
import {Icon, Label, Menu, Table, Form, Button} from 'semantic-ui-react';

export default class Stationsearch extends React.Component {

    constructor(props){
        super(props);
        this.state = {
            url: 'http://avwx.rest/api/metar/CYOW',
            data: {},
            Station: '',
            Timestamp: '',
            Time: '',
            FlightRules: '',
            Temperature: '',
            Dewpoint: '',
            Visibility: '',
            WindDir: '',
            WindGust: '',
            WindSpeed: '',
            WindVar: [],
            CloudList: [],
            Raw: '',
            userInput: '',
        };

        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    componentDidMount(){

        fetch(this.state.url)
        .then(d => d.json())
        .then(d => {
            this.setState({
                data: d, 
                Station: d.Station,
                Timestamp: d.Meta.Timestamp,
                Time: d.Time,
                FlightRules: d["Flight-Rules"],
                Temperature: d.Temperature,
                Dewpoint: d.Dewpoint,
                Visibility: d.Visibility,
                WindDir: d["Wind-Direction"],
                WindGust: d["Wind-Gust"],
                WindSpeed: d["Wind-Speed"],
                WindVar: d["Wind-Variable-Dir"],
                CloudList: d["Cloud-List"],
                Raw: d["Raw-Report"],
            })
        })
    }

    handleChange(event){
        this.setState({userInput: event.target.value});
    }

    handleSubmit(event){
        this.setState({url: getUrl(this.state.userInput)});
        event.preventDefault();
    }

render(){
    return (
        <div>
            <Form onSubmit={this.handleSubmit}>
                <Form.Field>
                    <Label>Station ID</Label>
                    <input type="text" placeholder="ex: CYOW" onChange={this.handleChange} />
                </Form.Field>
                <Button type='submit'>Search</Button>
            </Form>

            <Table celled striped textAlign="center" color="teal" key="teal" inverted className='table'>
                <Table.Header>
                    <Table.Row>
                    <Table.HeaderCell>Type</Table.HeaderCell>
                    <Table.HeaderCell>Value</Table.HeaderCell>
                    </Table.Row>
                </Table.Header>

                <Table.Body>
                    <Table.Row>
                        <Table.Cell>Station</Table.Cell>
                        <Table.Cell>{ this.state.Station }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Timestamp</Table.Cell>
                        <Table.Cell>{ this.state.Timestamp }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Zulu</Table.Cell>
                        <Table.Cell>{ this.state.Time }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Flight-Rules</Table.Cell>
                        <Table.Cell>{ this.state.FlightRules }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Temperature</Table.Cell>
                        <Table.Cell>{ this.state.Temperature }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Dewpoint</Table.Cell>
                        <Table.Cell>{ this.state.Dewpoint }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Visibility</Table.Cell>
                        <Table.Cell>{ this.state.Visibility }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Wind-Direction</Table.Cell>
                        <Table.Cell>{ this.state.WindDir }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Wind-Speed</Table.Cell>
                        <Table.Cell>{ this.state.WindSpeed }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Wind-Gust</Table.Cell>
                        <Table.Cell>{ this.state.WindGust }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Wind-Variable-Dir</Table.Cell>
                        <Table.Cell>{ this.state.WindVar }</Table.Cell>
                    </Table.Row>
                    <Table.Row>
                        <Table.Cell>Clouds</Table.Cell>
                        <Table.Cell>{ getCloudString(this.state.CloudList) }</Table.Cell>
                    </Table.Row>
                </Table.Body>
            </Table>
        </div>
    )}
}

function getCloudString(cloudList) {

    var list = "";

    for (var i=0; i < cloudList.length; i++){
        if (i+1 < cloudList.length){
            list += cloudList[i][0] + " " + cloudList[i][1] + ", ";
        }
        else{
            list += cloudList[i][0] + " " + cloudList[i][1];
        }
    }
    return list;
}

function getUrl(stationID){
    var url = 'http://avwx.rest/api/metar/';
    var id = stationID.toUpperCase();
    url += id;
    return url;
}

【问题讨论】:

    标签: javascript json reactjs api


    【解决方案1】:

    听起来您期望在url 更改时发出另一个fetch 请求。在您当前的代码中,您只在 componentDidMount 内执行 fetch ,它仅在首次创建组件时调用一次。相反,您应该在url 更改时调用它,无论是在componentDidUpdate 方法中还是在handleSubmit 中。

    【讨论】:

    • 谢谢你,这有效。但是,当我将其放入 handleUpdate() 函数时,表格中的值会在稳定之前闪烁一秒钟,而将其放入 handleSubmit() 函数时,我必须按两次 Enter 才能更改。知道为什么会这样吗?我假设它是因为值不是同步变化的?
    • 如果你把它放在handleSubmit 你需要确保状态已经改变,然后再进行获取。您可以通过在 setState 回调中执行此操作:this.setState({url: getUrl(this.state.userInput)}, () =&gt; { do the fetch here }) 您是否尝试在 componentDidUpdate 中调用它?如果是这样,请确保仅在 url 更改时调用 fetch,方法是将 previousState url 与新的 state url 进行比较。
    • @Ukranianolo 你确定正在发生闪烁吗?看看我创建的沙箱。我没有看到闪烁,或者我必须按两次 Enter。我认为由于 api 需要时间来获取数据,所以你有这样的印象。尝试一次输入电台 ID,然后按回车键一次即可。
    • 感谢您的帮助,它正在工作!是的,我会确保处理无效的输入。再次感谢。
    【解决方案2】:

    您只是在组件状态下更新 URL,而不是获取该站 id 的数据。更新站 ID 后,您还需要获取数据并在状态下更新它。您的组件正在重新渲染,但由于任何数据都没有改变,因此您的视图没有得到更新。

      fetchData = () => {
        fetch(this.state.url)
          .then(d => d.json())
          .then(d => {
            this.setState({
              data: d,
              Station: d.Station,
              Timestamp: d.Meta.Timestamp,
              Time: d.Time,
              FlightRules: d["Flight-Rules"],
              Temperature: d.Temperature,
              Dewpoint: d.Dewpoint,
              Visibility: d.Visibility,
              WindDir: d["Wind-Direction"],
              WindGust: d["Wind-Gust"],
              WindSpeed: d["Wind-Speed"],
              WindVar: d["Wind-Variable-Dir"],
              CloudList: d["Cloud-List"],
              Raw: d["Raw-Report"]
            });
          });
      };
    
      componentDidMount() {
        this.fetchData();
      }
    
      handleSubmit(event) {
        this.setState({ url: getUrl(this.state.userInput) }, () => {
          this.fetchData();
        });
        event.preventDefault();
      }
    

    这里是工作代码的链接。我已将默认电台 ID 设为 OMDB,当您搜索 CYOW 数据时,数据会更新。

    https://codesandbox.io/s/3mqp187w1

    【讨论】:

    • 非常感谢,我现在明白了。但是,您能否解释一下您更改的 handleSubmit 函数的语法含义?我是 JavaScript 新手。 handleSubmit(event){ this.setState({url: getUrl(this.state.userInput) }, () =&gt; { this.fetchData(); }); event.preventDefault(); }
    • 变化是给setState函数一个新的回调函数,获取新的信息。 setState 实际上是异步的,所以我们不能在调用它后立即尝试访问状态,除非你通过函数接受的回调来做到这一点。
    【解决方案3】:

    代码运行良好,只是提交时不再获取!

    这是一个工作版本:

    https://codesandbox.io/s/k902vrw7k7

    还要确保验证用户输入,因为现在当他们输入无效电台时,您会崩溃。

    【讨论】:

    • 谢谢,我明白了。