rnd om
rnd om

Reputation: 355

how do i track state when there are multiple selections

i have a scenario where a user has three options:

he can choose the body_type of a car, the car_year it was made, or the car_make of it.

starting out, i have a state carJson that is set to a list of cars.

"2": {
   "attrs": {
      "car_year": 2021,
      "car_make": "Volvo",
      "car_model": "XC60",
      "car_trim": "T8 Plug-In Hybrid Inscription Expression eAWD",
      "body_type": "SUV"
   }
},
"4": {
   "attrs": {
      "car_year": 2020,
      "car_make": "Honda",
      "car_model": "Fit",
      "car_trim": "LX Manual",
      "body_type": "hatchback"
   }
},
"5": {
   "attrs": {
      "car_year": 2020,
      "car_make": "Jeep",
      "car_model": "Renegade",
      "car_trim": "Trailhawk 4WD",
      "body_type": "SUV"
   }
},
"6": {
   "attrs": {
      "car_year": 2022,
      "car_make": "Kia",
      "car_model": "Forte",
      "car_trim": "FE Manual",
      "body_type": "sedan"
   }
},

if the user chooses the body_type, i have setCarJson update to only include cars that have a matching body type.

from there, the car_year and car_make options are updated to only show those filtered cars. but the body_type options remain the same, so that the user can change his choice.

so for example, the user chooses 'SUV' - so the years available to select from will be {2020,2021}. pretty straight forward.

but now, let's say the user chooses 2021. the make should only show {volvo} and the car body_type choices shouldn't change. so there should be three different states, right?

it could very well be that the user starts off with the make or the year, instead of the body type - so i want that first choice to offer the original options.

diagram

and finally to structure it so that let's say the user deselects body_type after choosing car_year - it will update to show only body_types that fall within that selected year.

so basically, three selections, that progressively/regressively filter each other out.

just looking for some kind of direction on how to think about this. thanks.

Upvotes: 1

Views: 51

Answers (1)

Adam Glasser
Adam Glasser

Reputation: 89

How about this?

https://react-wj8s1v.stackblitz.io/

In case that stack blitz doesn't work, here is my code

import React, { useState, useEffect } from 'react';

export default function App() {

  // Initial Data
  const cars = {
    2: {
      attrs: {
        car_year: 2021,
        car_make: 'Volvo',
        car_model: 'XC60',
        car_trim: 'T8 Plug-In Hybrid Inscription Expression eAWD',
        body_type: 'SUV',
      },
    },
    4: {
      attrs: {
        car_year: 2020,
        car_make: 'Honda',
        car_model: 'Fit',
        car_trim: 'LX Manual',
        body_type: 'hatchback',
      },
    },
    5: {
      attrs: {
        car_year: 2020,
        car_make: 'Jeep',
        car_model: 'Renegade',
        car_trim: 'Trailhawk 4WD',
        body_type: 'SUV',
      },
    },
    6: {
      attrs: {
        car_year: 2022,
        car_make: 'Kia',
        car_model: 'Forte',
        car_trim: 'FE Manual',
        body_type: 'sedan',
      },
    },
  };

  // state to house our new filtered option
  const [filteredCars, setFilteredCars] = useState(cars);
  
  // all of our cars
  const carIndexes = Object.keys(filteredCars);
  
  // create select options for make, year, and body
  const bodySelects = () => {
    const bodies = [];
    carIndexes.forEach((index) => {
      bodies.push(filteredCars[index].attrs.body_type);
    });
    return bodies.map((x, y) => <option key={y}>{x}</option>);
  };

  const yearSelects = () => {
    const years = [];
    carIndexes.forEach((index) => {
      years.push(filteredCars[index].attrs.car_year);
    });
    return years.map((x, y) => <option key={y}>{x}</option>);
  };

  const makeSelects = () => {
    const makes = [];
    carIndexes.forEach((index) => {
      makes.push(filteredCars[index].attrs.car_make);
    });
    return makes.map((x, y) => <option key={y}>{x}</option>);
  };

  // On select filter the array
  const onSelect = e => {
    const newFiltered = [];
    const type = e.currentTarget.dataset.type;
    carIndexes.forEach((index) => {
      if (filteredCars[index].attrs[type] == e.currentTarget.value){
        newFiltered.push(filteredCars[index])
      };
    });
    setFilteredCars({...newFiltered})
  };

  return (
    <>
    <div>
      <select onChange={onSelect} data-type="body_type">
        <option selected>Pick Body</option>
        {bodySelects()}
      </select>
      <select onChange={onSelect} data-type="car_year">
        <option selected>Pick Year</option>
        {yearSelects()}
      </select>
      <select onChange={onSelect} data-type="car_make">
        <option selected>Pick Make</option>
        {makeSelects()}
      </select>
      <button onClick={() => setFilteredCars(cars)}>reset options</button>
    </div>
    <p>{filteredCars ? JSON.stringify(filteredCars) : ''}</p>
    </>
  );
}

Upvotes: 1

Related Questions