繁体   English   中英

React 持有不超过一个数组元素的 state

[英]React holds state of no more than one array element

我已经停止制作这个 covid19 应用程序了添加的国家。 我对 React 也有点陌生。

问题是,当我单击添加按钮时,添加的 state 会更新,并在屏幕右侧显示添加的国家/地区。 但是,当我尝试添加另一个国家/地区时,出现错误。 我相信当我尝试从 App.js 中的 addCountry 方法中 setState({ state }) 时,错误就在某个地方。

换句话说,“添加”的 state 只会让自己容纳不超过一个数组元素。 非常感谢帮助。 我发布了所有代码。

index.js

import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

应用程序.js

import CountryList from "./components/CountryList.js";
import Find from "./components/Find.js";
import Added from "./components/Added.js";

class App extends Component {
  constructor() {
    super();
    this.state = {
      countries: [],
      inputbox: [],
      added: [],
    };
  }

  // Arrow functions capture "this" when they are defined, while standard functions do when they are executed.
  // Thus, no need for the bind method. Awesome.
  handleChange = (e) =>
    this.setState({
      inputbox: e.target.value,
    });

  getCountryData = async (slug) => {
    const resp = await fetch(`https://api.covid19api.com/live/country/${slug}`);
    var addedData = await resp.json();
    // Api returns most days of covid, per country, that it tracks
    // Thus, we want the last tracked day of a country
    addedData = addedData[addedData.length - 1];
    return addedData;
  };

  // Add a country to the added state
  // Call when user clicks button associated with their desired country
  addCountry = async (btnId) => {
    const { countries, added } = this.state;
    var addedData = await this.getCountryData(btnId);
    countries.map((country) => {
      // If the button ID is equal to the current country in the loops' Slug
      if (btnId == country.Slug) {
        try {
          added.push([
            {
              addedCountry: addedData.Country,
              confirmedTotal: addedData.Confirmed,
              deathsTotal: addedData.Deaths,
              recoveredTotal: addedData.Recovered,
              activeTotal: addedData.Active,
            },
          ]);

          // (bug) IT IS PUSHING, BUT ITS NOT SETTING THE STATE!
          // ITS ONLY LETTING ME KEEP ONE ITEM IN THE STATE
          this.setState({ added });
          console.log(added);
        } catch (error) {
          alert(`Sorry, country data not available for ${country.Country}`);
          return;
        }
      }
    });
  };

  removeCountry = (btnId) => {
    const { added } = this.state;
    added.map((added, index) => {
      //console.log(added[index].addedCountry);
      if (btnId == added[index].addedCountry) {
        added.splice(index, 1);
        this.setState({ added: added });
      } else {
        console.log("not removed");
        return;
      }
    });
  };

  // Mount-on lifecycle method
  async componentDidMount() {
    const resp = await fetch("https://api.covid19api.com/countries");
    const countries = await resp.json(); // parsed response
    this.setState({ countries }); // set state to parsed response
  }

  render() {
    // Filter out countries depending on what state the inputbox is in
    const { countries, inputbox } = this.state;
    const filtered = countries.filter((country) =>
      country.Country.includes(inputbox)
    );

    return (
      <div className="App Container">
        <Find
          placeholder="Type to find a country of interest..."
          handleChange={this.handleChange}
        />
        <div className="row">
          <CountryList countries={filtered} addCountry={this.addCountry} />
          <Added added={this.state.added} removeCountry={this.removeCountry} />
        </div>
      </div>
    );
  }
}

export default App;
Added.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
import AddedCountry from "./AddedCountry.js";

class Added extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className="col-md-6">
        <Table>
          <thead>
            <tr>
              <th scope="col">#</th>
              <th scope="col">Country</th>
              <th scope="col">Active</th>
              <th scope="col">Confirmed Total</th>
              <th scope="col">Recovered</th>
              <th scope="col">Deaths</th>
              <th scope="col">Action</th>
            </tr>
          </thead>

          {this.props.added.map((added, index) => (
            <AddedCountry
              added={added[index]}
              removeCountry={this.props.removeCountry}
            />
          ))}
        </Table>
      </div>
    );
  }
}

export default Added;
AddedCountry.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";

class AddedCountry extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <tbody>
        <tr>
          <td></td>
          <td>{this.props.added.addedCountry}</td>
          <td>{this.props.added.activeTotal}</td>
          <td>{this.props.added.confirmedTotal}</td>
          <td>{this.props.added.recoveredTotal}</td>
          <td>{this.props.added.deathsTotal}</td>
          <td>
            {
              <Button
                onClick={() =>
                  this.props.removeCountry(
                    document.getElementById(this.props.added.addedCountry).id
                  )
                }
                id={this.props.added.addedCountry}
                type="submit"
                color="danger"
                size="sm"
              >
                Remove
              </Button>
            }
          </td>
        </tr>
      </tbody>
    );
  }
}

export default AddedCountry;
CountryList.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
import Country from "./Country.js";

class CountryList extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className="col-md-6">
        <Table>
          <thead>
            <tr>
              <th scope="col">#</th>
              <th scope="col">Country</th>
              <th scope="col">Actions</th>
            </tr>
          </thead>

          {
            // Each country is a component
            // Function will display all countries as the Map function loops through them
            this.props.countries.map((country) => (
              <Country countries={country} addCountry={this.props.addCountry} />
            ))
          }
        </Table>
      </div>
    );
  }
}

export default CountryList;
Country.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";

class Country extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <tbody>
        <tr>
          <td></td>
          <td>{this.props.countries.Country}</td>
          <td>
            {
              <Button
                onClick={() =>
                  this.props.addCountry(
                    document.getElementById(this.props.countries.Slug).id
                  )
                }
                id={this.props.countries.Slug}
                type="submit"
                color="success"
                size="sm"
              >
                Add
              </Button>
            }
          </td>
        </tr>
      </tbody>
    );
  }
}

export default Country;
Find.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";

class Find extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className="Find container">
        <br />
        <Form>
          <div className="form-row">
            <div className="form-group col-md-6">
              <h3>Find a Country</h3>
              <Input
                type="text"
                className="form-control"
                id="country"
                placeholder={this.props.placeholder}
                onChange={this.props.handleChange}
              ></Input>
            </div>
          </div>
        </Form>
      </div>
    );
  }
}

export default Find;

我没有仔细研究所有这些代码,但是将重点放在您认为问题所在的地方,很明显您正在通过直接推入added的数组来改变您的 state object 。

解决方案

不要改变状态!

由于您似乎只想添加一个新的“添加”并且仅当按钮的btnId与国家/地区的 slug 匹配时,并且btnId只能是映射countries地区数组中的有效值,我认为这可以大大简化。

addCountry = async (btnId) => {
  const addedData = await this.getCountryData(btnId);

  if (addedData) {
    this.setState(prevState => ({
      added: prevState.added.concat({ // <-- concat creates a new array reference
        addedCountry: addedData.Country,
        confirmedTotal: addedData.Confirmed,
        deathsTotal: addedData.Deaths,
        recoveredTotal: addedData.Recovered,
        activeTotal: addedData.Active,
      }),
    }));
  } else {
    alert(`Sorry, country data not available for ${country.Country}`);
  }
};

同样, removeCountry处理程序误用了数组映射 function 并改变了added的 state。 Array.prototype.filter是从数组中删除元素并返回新数组引用的惯用方式。

removeCountry = (btnId) => {
  this.setState(prevState => ({
    added: prevState.added.filter(el => el.addedCountry !== btnId),
  }));
};

其他问题和建议

添加了.js

如果您将added的数组维护为平面数组(而不是数组数组),那么对于 map 的值来说是微不足道的。

{this.props.added.map((added) => (
  <AddedCountry
    key={added}
    added={added}
    removeCountry={this.props.removeCountry}
  />
))}

Country.js 和添加的Country.js

当您确实就在那儿并且可以在onClick回调中包含 country slug 时,我看不出有任何理由查询 DOM 以获取按钮 ID。

<Button
  onClick={() => this.props.addCountry(this.props.countries.Slug)}
  id={this.props.countries.Slug}
  type="submit"
  color="success"
  size="sm"
>
  Add
</Button>

<Button
  onClick={() => this.props.removeCountry(this.props.added.addedCountry)}
  id={this.props.added.addedCountry}
  type="submit"
  color="danger"
  size="sm"
>
  Remove
</Button>

应用程序.js

这可能或可能无关紧要,但通常是对数据进行不区分大小写的搜索/过滤。 这是为了确保像“France”这样的内容仍然匹配用户的“france”搜索输入。

const filtered = countries.filter((country) =>
  country.Country.toLowerCase().includes(inputbox.toLowerCase())
);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM