Home > Mobile >  How to fix product filtering on React?
How to fix product filtering on React?

Time:04-02

I have React components :

Main.jsx

import React from "react";
import { Products } from "../Products";
import { Filter } from "../Filter";

class Main extends React.Component {
  state = {
    products: [],
    filteredProducts: [],
    status: "all",
  };

  onFilterStatusChange = (status) => {
    this.setState({ status });
  };

  componentDidMount() {
    fetch("./products.json")
      .then((responce) => responce.json())
      .then((data) => this.setState({ products: Object.values(data) }));
  }

  filterProducts() {
    this.setState(({ status }) => ({
      filteredProducts:
        status === "all"
          ? this.state.products
          : this.state.products.filter((n) => n.prod_status?.includes(status)),
    }));
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.state.status !== prevState.status ||
      this.state.products !== prevState.products
    ) {
      this.filterProducts();
    }
  }

  render() {
    return (
      <main className="container content">
        <Filter
          values={[
            "all",
            "recommended",
            "promotion",
            "saleout",
            "bestseller",
            "new",
          ]}
          value={this.state.status}
          onChange={this.onFilterStatusChange}
        />
        <Products products={this.state.filteredProducts} />
      </main>
    );
  }
}

export { Main };

Filter.jsx

import statusMap from "../statusMap";

const Filter = ({ values, value, onChange }) => (
  <div className="row filter_container">
    <h3 className="filter_title">Sortować według</h3>
    <div className="input-field col s12">
      <select className="status_select">
        {values.map((n) => (
          <option onChange={() => onChange(n)} value={value === n} key={n}>
            {statusMap.get(n)}
          </option>
        ))}
      </select>
    </div>
  </div>
);

export { Filter };

The problem is that the select does not respond to value changes, and therefore does not show the filtered products. I think I messed up with the properties

The filter used to work with type ="radio". After I tried to redo the filter (change type="radio" to ), he stopped working. This is what Filter.jsx looked like before changing to select:

{values.map((n) => (
        <label>
          <input
            className="with-gap"
            type="radio"
            onChange={() => onChange(n)}
            checked={value === n}
          />
          <span>{statusMap.get(n)}</span>
        </label>
      ))}

Please, help me fix this filter

CodePudding user response:

It worked with radio inputs because inputs handle directly the onChange event listener, if you want to use a select, you need to move the onChange on the select and use it like this:

<select className="status_select" onChange={(e) => onChange(e.target.value)}>
        {values.map((n) => (
          <option value={value} key={n}>
            {statusMap.get(n)}
          </option>
        ))}
      </select>

You can check an example of how to handle a select properly here: demo.

CodePudding user response:

There are a few issues in your code:

  • The onChange event is triggered on the select element, not on the option element.
  • value={value === n}, according to your code, value is a string which I assume that represents the default value of the select, and n is the current value inside the map function, meaning that the value property is a boolean, but you need it to be a unique value for each option. As for the default select value - in React you can use the selected property
  • I also suggest you to use the index for the key property.

I made some tweaks to your code to try to fix your problem, or at least help you to find it. I can't test it as I don't have the statusMap so its on you.

import React from "react";

class App extends React.Component {
  state = {
    products: [],
    filteredProducts: [],
    status: "all",
  };

  onFilterStatusChange = (status) => {
    this.setState({ status });
  };

  componentDidMount() {
    fetch("./products.json")
      .then((responce) => responce.json())
      .then((data) => this.setState({ products: Object.values(data) }));
  }

  filterProducts() {
    this.setState(({ status }) => ({
      filteredProducts:
        status === "all"
          ? this.state.products
          : this.state.products.filter((n) => n.prod_status?.includes(status)),
    }));
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.state.status !== prevState.status ||
      this.state.products !== prevState.products
    ) {
      this.filterProducts();
    }
  }

  render() {
    return (
      <main className="container content">
        <Filter
          values={[
            "all",
            "recommended",
            "promotion",
            "saleout",
            "bestseller",
            "new",
          ]}
          value={this.state.status}
          onFilterChange={this.onFilterStatusChange}
        />
        <Products products={this.state.filteredProducts} />
      </main>
    );
  }
}

export { App };


const Filter = ({ values, currentValue, onFilterChange }) => (
  <div className='row filter_container'>
    <h3 className='filter_title'>Sortować według</h3>
    <div className='input-field col s12'>
      <select
        className='status_select'
        onChange={(e) => onFilterChange(e.target.value)}
        selected={currentValue}
      >
        {values.map((val, index) => (
          <option value={val} key={index}>
            {statusMap.get(val)}
          </option>
        ))}
      </select>
    </div>
  </div>
);

  • Related