簡體   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