admin管理员组

文章数量:1279085

Upon clicking a filter button, the first filter returns the correct data, but the array in state is mutated (or destroyed?) when a second filter method is called. I'm using setState for each function and cannot figure out why the array's initial state is not restored.

This app is working when I import a local copy of the JSON data. The blocks of mented code do indeed show that the filter functions work with the local data, but not with the same JSON data returned by the fetch method from my server.

import React from "react";
import { Button, Row, Col } from "react-bootstrap";
import AutoList from './AutoList';
// import autoData from '../autoData.json';

const API = "https://MY_SERVER/autoData.json";

class GetAutos extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      autos: [],
    };
  }

  fetchAutoData() {
      fetch(API)
        .then(autoData => autoData.json())
        .then(autoData =>
            this.setState({
                autos: autoData, 
                isLoading: false,
            })
        )
        .catch(error => this.setState({ error, isLoading: false }));

  }

  ponentDidMount() {
      this.fetchAutoData();
      // this.setState(() => ({ autos: autoData }));
  }


  render() {

    const { autos } = this.state;

    let onResetArray = () => {
        this.setState({ autoData: this.state.autos })
        //console.log(autos)
      }

    let filterFord = () => {
        const fordAutos = autos.filter( (auto) => auto.title.includes("Ford"));
        // const fordAutos = autoData.filter( (auto) => auto.title.includes("Ford"));

        this.setState({ autos: fordAutos });
    }


    let filterChevy = () => {
        const chevyAutos = autos.filter( (auto) => auto.title.includes("Chevrolet"));
        // const chevyAutos = autoData.filter( (auto) => auto.title.includes("Chevrolet"));

        this.setState({ autos: chevyAutos });
    }

    let filterJeep = () => {
        const jeepAutos = autos.filter( (auto) => auto.title.includes("Jeep"));
        // const jeepAutos = autoData.filter( (auto) => auto.title.includes("Jeep"));

        this.setState({ autos: jeepAutos });
    }
    return (
        <div className="container">
            <Row className="mb-4">
                <Col>
                    <Button event-key="reset-autos" onClick={onResetArray}>All Cars</Button>
                </Col>
                <Col>
                    <Button event-key="ford-autos" onClick={filterFord}>Filter Ford</Button>
                </Col>
                <Col>
                    <Button event-key="chevy-autos" onClick={filterChevy}>Filter Chevy</Button>
                </Col>
                <Col>
                    <Button event-key="jeep-autos" onClick={filterJeep}>Filter Jeep</Button>
                </Col>
            </Row>


        <AutoList autos={ this.state.autos } />
        </div>

    );
  }
}
export default GetAutos;

I expect a subsequent button click to filter the data set in state. Instead, the click filters the array created by the .filter() method. My code works with a local import of my JSON data, but not with the fetched JSON.

Upon clicking a filter button, the first filter returns the correct data, but the array in state is mutated (or destroyed?) when a second filter method is called. I'm using setState for each function and cannot figure out why the array's initial state is not restored.

This app is working when I import a local copy of the JSON data. The blocks of mented code do indeed show that the filter functions work with the local data, but not with the same JSON data returned by the fetch method from my server.

import React from "react";
import { Button, Row, Col } from "react-bootstrap";
import AutoList from './AutoList';
// import autoData from '../autoData.json';

const API = "https://MY_SERVER/autoData.json";

class GetAutos extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      autos: [],
    };
  }

  fetchAutoData() {
      fetch(API)
        .then(autoData => autoData.json())
        .then(autoData =>
            this.setState({
                autos: autoData, 
                isLoading: false,
            })
        )
        .catch(error => this.setState({ error, isLoading: false }));

  }

  ponentDidMount() {
      this.fetchAutoData();
      // this.setState(() => ({ autos: autoData }));
  }


  render() {

    const { autos } = this.state;

    let onResetArray = () => {
        this.setState({ autoData: this.state.autos })
        //console.log(autos)
      }

    let filterFord = () => {
        const fordAutos = autos.filter( (auto) => auto.title.includes("Ford"));
        // const fordAutos = autoData.filter( (auto) => auto.title.includes("Ford"));

        this.setState({ autos: fordAutos });
    }


    let filterChevy = () => {
        const chevyAutos = autos.filter( (auto) => auto.title.includes("Chevrolet"));
        // const chevyAutos = autoData.filter( (auto) => auto.title.includes("Chevrolet"));

        this.setState({ autos: chevyAutos });
    }

    let filterJeep = () => {
        const jeepAutos = autos.filter( (auto) => auto.title.includes("Jeep"));
        // const jeepAutos = autoData.filter( (auto) => auto.title.includes("Jeep"));

        this.setState({ autos: jeepAutos });
    }
    return (
        <div className="container">
            <Row className="mb-4">
                <Col>
                    <Button event-key="reset-autos" onClick={onResetArray}>All Cars</Button>
                </Col>
                <Col>
                    <Button event-key="ford-autos" onClick={filterFord}>Filter Ford</Button>
                </Col>
                <Col>
                    <Button event-key="chevy-autos" onClick={filterChevy}>Filter Chevy</Button>
                </Col>
                <Col>
                    <Button event-key="jeep-autos" onClick={filterJeep}>Filter Jeep</Button>
                </Col>
            </Row>


        <AutoList autos={ this.state.autos } />
        </div>

    );
  }
}
export default GetAutos;

I expect a subsequent button click to filter the data set in state. Instead, the click filters the array created by the .filter() method. My code works with a local import of my JSON data, but not with the fetched JSON.

Share Improve this question asked Jul 29, 2019 at 21:11 J. GundersonJ. Gunderson 671 gold badge3 silver badges13 bronze badges 1
  • you need to not apply the filter on the state array itself, instead make a copy of the state array. const autos = [...this.state.autos]. Next you need to keep them in a different array so you dont lose the data. I would put those filter methods on the class instead of defining them in the render also – John Ruddell Commented Jul 29, 2019 at 21:13
Add a ment  | 

3 Answers 3

Reset to default 3

When you set the state with the filtered result you are removing entries from the source. You should instead store the filtered result in a different key.

For instance something like this.

const jeepAutos = autos.filter( (auto) => auto.title.includes("Jeep"))
this.setState({
  filteredAutos: jeepAutos
})

Then when rendering you would use it like so

<AutoList autos={ this.state.filteredAutos } />

Remember that initially filteredAutos would be empty, so to view all you would need to check for filtered items before passing

<AutoList autos={ filteredAutos.length > 0 ? filteredAutos : autos } />

Simple example to play with

JSON File:

[
  {
    "id": 1,
    "name": "Maruti Ritz \\Hatchback Manual",
    "details": "5 seater, petrol",
    "picture": "./static/images/Ritz.jpg",
    "overcharge": "PKR 130 per excess km",
    "weekday": "PKR 120/hr",
    "weekend": "PKR 157/hr",
    "car_type": "Hatchback"
  },
  {
    "id": 2,
    "name": "Toyota Corolla XLI \\Hatchback Automatic",
    "details": "4 seater, diesel",
    "picture": "./static/images/XLI.jpg",
    "overcharge": "PKR 179 per excess km",
    "weekday": "PKR 190/hr",
    "weekend": "PKR 287/hr",
    "car_type": "SUV"
  },

constructor(props) {
    super(props);
    this.state = {
      lists: [],
}
}
  ponentDidMount() {
    axios.get('http://localhost:3001/your directory/yourfilename.json')
      .then(response => {
      // console.log(response.data)
       this.setState({
         lists: response.data,
         isLoading: false
        })
    })
 }

render() {

    const {lists} = this.state;
    console.log(lists)
     let filterHatchback = () => {
      // alert("event clicked")
      const hatchbackCar = lists.filter( (list) => list.car_type === 'Hatchback');
      this.setState({ lists: hatchbackCar });
      };

       return(
             <Card
                  className="carder"
                  body
                  style={{ fontSize: "14px", height: "" }}
                >
                  <p>Car Type</p>
                  <Button outline color="secondary" 
                    onClick={filterHatchback}
                    >
                    Hatchback
                  </Button> and so on

I think you would like to display autos, and keep the unfiltered API results in autoData. So:

  1. create a state for both

    this.state = {
          autos: [],
          audoData: [],
    };
    
  2. Store the API result in autoData:

    this.setState({ autoData, isLoading: false, })

  3. Filter the autoData instead of autos:

const { autoData } = this.state; const fordAutos = autoData.filter( (auto) => auto.title.includes("Ford"));

本文标签: javascriptHow to filter JSON data from API and setState in ReactStack Overflow