2

I have this checkbox input, when i click on it, i take its value and add to state array called vendorFilters but seems like state is always 1 step behind, i was using class component i thought switching to hooks might solve the problem but it did not.

I have attached codesandbox link, kindly have a look.

https://codesandbox.io/s/naughty-brattain-89hxi?file=/src/App.js

import React, { useState, useEffect } from "react";
import "./styles.css";
import raw from "./data.json";

const App = () => {
  const [data, setData] = useState([]);
  const [vendorFilters, setVendorFilters] = useState([]);
  const [capacityFilters, setCapacityFilters] = useState([]);
  const [speedFilters, setSpeedFilters] = useState([]);
  const [cycleLatencyFilters, setCycleLatencyFilters] = useState([]);
  const [colorFilters, setColorFilters] = useState([]);
  const [vendorQuantity, setVendorQuantity] = useState([0, 0, 0, 0, 0, 0]);

  useEffect(() => setData(raw), []);

  const inputHandler = event => {
    updateFiltersArrays(
      event.target.value,
      event.target.name,
      event.target.checked
    );
    console.log(vendorFilters);
    filterFunction();
  };

  const filterFunction = () => {
    const filteredData1 = data.filter(object => {
      if (vendorFilters.length === 0) return true;
      return vendorFilters.includes(object.vendor);
    });
    const filteredData2 = filteredData1.filter(({ capacity }) => {
      if (capacityFilters.length === 0) return true;
      return capacityFilters.includes(capacity.toString());
    });
    const filteredData3 = filteredData2.filter(({ speed }) => {
      if (speedFilters.length === 0) return true;
      return speedFilters.includes(speed.toString());
    });
    const filteredData4 = filteredData3.filter(({ cycleLatency }) => {
      if (cycleLatencyFilters.length === 0) return true;
      return cycleLatencyFilters.includes(cycleLatency.toString());
    });
    const filteredData5 = filteredData4.filter(({ color }) => {
      if (colorFilters.length === 0) return true;
      return colorFilters.includes(color.toString());
    });
    console.log("hikhan");
    const vendor = [0, 0, 0, 0, 0, 0];
    vendor[0] = filteredData5.filter(
      object => object.vendor === "BESTRAM" && vendorFilters.includes("BESTRAM")
    ).length;
  };

  const updateFiltersArrays = (input, collection, checked) => {
    switch (collection) {
      case "vendor":
        if (checked) {
          setVendorFilters([...vendorFilters, input]);
        } else {
          const filters = vendorFilters.filter(f => f !== input);
          setVendorFilters(filters);
        }

        break;
      case "capacity":
        if (checked) {
          setCapacityFilters(prev => [...prev, input]);
        } else {
          const filters = vendorFilters.filter(f => f !== input);
          setCapacityFilters(filters);
        }

        break;
      case "speed":
        if (checked) {
          setSpeedFilters(prev => [...prev, input]);
        } else {
          const filters = vendorFilters.filter(f => f !== input);
          setSpeedFilters(filters);
        }

        break;
      case "cycleLatency":
        if (checked) {
          setCycleLatencyFilters(prev => [...prev, input]);
        } else {
          const filters = vendorFilters.filter(f => f !== input);
          setCycleLatencyFilters(filters);
        }

        break;
      case "color":
        if (checked) {
          setColorFilters(prev => [...prev, input]);
        } else {
          const filters = vendorFilters.filter(f => f !== input);
          setColorFilters(filters);
        }

        break;
      default:
        break;
    }
  };

  return (
    <div class="main-content">
      <h1>Filter Predictions</h1>
      <div class="filter">
        <div class="group" data-test="vendor-group">
          <div class="group__name">vendor</div>
          <label class="group__item" data-test="item-BESTRAM">
            <input
              type="checkbox"
              value="BESTRAM"
              name="vendor"
              onClick={inputHandler}
            />
            BESTRAM ({vendorQuantity[0]})
          </label>
          <label class="group__item" data-test="item-Rocket">
            <input
              type="checkbox"
              value="Rocket"
              name="vendor"
              onClick={inputHandler}
            />
            Rocket ()
          </label>
          <label class="group__item" data-test="item-gTech">
            <input
              type="checkbox"
              value="gTech"
              name="vendor"
              onClick={inputHandler}
            />
            gTech ({vendorQuantity[2]})
          </label>
          <label class="group__item" data-test="item-5Byte">
            <input
              type="checkbox"
              value="5Byte"
              name="vendor"
              onClick={inputHandler}
            />
            5Byte ({vendorQuantity[3]})
          </label>
          <label class="group__item" data-test="item-Xdata">
            <input
              type="checkbox"
              value="Xdata"
              name="vendor"
              onClick={inputHandler}
            />
            Xdata ({vendorQuantity[4]})
          </label>
          <label class="group__item" data-test="item-Abc">
            <input
              type="checkbox"
              value="Abc"
              name="vendor"
              onClick={inputHandler}
            />
            Abc ({vendorQuantity[5]})
          </label>
        </div>
        <div class="group" data-test="capacity-group">
          <div class="group__name">capacity</div>
          <label class="group__item" data-test="item-4">
            <input
              type="checkbox"
              value={4}
              name="capacity"
              onClick={inputHandler}
            />
            4 GB ()
          </label>
          <label class="group__item" data-test="item-8">
            <input
              type="checkbox"
              value={8}
              name="capacity"
              onClick={inputHandler}
            />
            8 GB ()
          </label>
          <label class="group__item" data-test="item-32">
            <input
              type="checkbox"
              value={32}
              name="capacity"
              onClick={inputHandler}
            />
            32 GB ()
          </label>
          <label class="group__item" data-test="item-16">
            <input
              type="checkbox"
              value={16}
              name="capacity"
              onClick={inputHandler}
            />
            16 GB ()
          </label>
        </div>
        <div class="group" data-test="speed-group">
          <div class="group__name">speed</div>
          <label class="group__item" data-test="item-2400">
            <input
              type="checkbox"
              value={2400}
              name="speed"
              onClick={inputHandler}
            />
            2400 MHz (80)
          </label>
          <label class="group__item" data-test="item-2666">
            <input
              type="checkbox"
              value={2666}
              name="speed"
              onClick={inputHandler}
            />
            2666 MHz (30)
          </label>
          <label class="group__item" data-test="item-3333">
            <input
              type="checkbox"
              value={3333}
              name="speed"
              onClick={inputHandler}
            />
            3333 MHz (25)
          </label>
          <label class="group__item" data-test="item-3200">
            <input
              type="checkbox"
              value={3200}
              name="speed"
              onClick={inputHandler}
            />
            3200 MHz (23)
          </label>
          <label class="group__item" data-test="item-3600">
            <input
              type="checkbox"
              value={3600}
              name="speed"
              onClick={inputHandler}
            />
            3600 MHz (21)
          </label>
          <label class="group__item" data-test="item-4000">
            <input
              type="checkbox"
              value={4000}
              name="speed"
              onClick={inputHandler}
            />
            4000 MHz (21)
          </label>
        </div>
        <div class="group" data-test="cycle-latency-group">
          <div class="group__name">cycle latency</div>
          <label class="group__item" data-test="item-CL-7">
            <input
              type="checkbox"
              value={"CL 7"}
              name="cycleLatency"
              onClick={inputHandler}
            />
            CL 7 (54)
          </label>
          <label class="group__item" data-test="item-CL-9">
            <input
              type="checkbox"
              value={"CL 9"}
              name="cycleLatency"
              onClick={inputHandler}
            />
            CL 9 (51)
          </label>
          <label class="group__item" data-test="item-CL-12">
            <input
              type="checkbox"
              value={"CL 12"}
              name="cycleLatency"
              onClick={inputHandler}
            />
            CL 12 (51)
          </label>
          <label class="group__item" data-test="item-CL-11">
            <input
              type="checkbox"
              value={"CL 11"}
              name="cycleLatency"
              onClick={inputHandler}
            />
            CL 11 (44)
          </label>
        </div>
        <div class="group" data-test="color-group">
          <div class="group__name">color</div>
          <label class="group__item" data-test="item-Green">
            <input
              type="checkbox"
              value={"Green"}
              name="color"
              onClick={inputHandler}
            />
            Green (94)
          </label>
          <label class="group__item" data-test="item-Black">
            <input
              type="checkbox"
              value={"Black"}
              name="color"
              onClick={inputHandler}
            />
            Black (50)
          </label>
          <label class="group__item" data-test="item-Red">
            <input
              type="checkbox"
              value={"Red"}
              name="color"
              onClick={inputHandler}
            />
            Red (31)
          </label>
          <label class="group__item" data-test="item-White">
            <input
              type="checkbox"
              value={"White"}
              name="color"
              onClick={inputHandler}
            />
            White (25)
          </label>
        </div>
      </div>
    </div>
  );
};

export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Zargham Khan
  • 254
  • 3
  • 12

2 Answers2

3

setState is an asynchronous function, and hence the value of array may not update just yet. It will have the updated value in next render cycle.

Solution would be to use useEffect react hook

 const inputHandler = event => {
    updateFiltersArrays(
      event.target.value,
      event.target.name,
      event.target.checked
    );
    // remove function call from here
  };
const filterFunction = useCallback(()=>{
  // rest of the code
},[])

useEffect(()=>{
  console.log(vendorFilters); // will have updated value
  filterFunction()
},[vendorFilters,filterFunction])

The above code states that whenever vendorFilter changes, run this function

Sarthak Aggarwal
  • 2,284
  • 1
  • 8
  • 12
  • thanks, but it gives warning that filterfunction dependency is missing. – Zargham Khan Jul 20 '20 at 12:22
  • @ZarghamKhan I have updated my code. To know more about it, read https://stackoverflow.com/a/55854902 – Sarthak Aggarwal Jul 20 '20 at 12:27
  • after doing this change, it says filterFunction was used before it was defined. – Zargham Khan Jul 20 '20 at 12:37
  • and The 'filterFunction' function makes the dependencies of useEffect Hook (at line 19) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'filterFunction' definition into its own useCallback() Hook. – Zargham Khan Jul 20 '20 at 12:38
  • There are basically two ways to remove the warning, First is to move your whole filter function inside `useEffect` if you are not using it anywhere else, 2nd is to wrap your function with `useCallback` like i did in the code. Also filter function must be defined before this useEffect since it is using it in its dependencies. – Sarthak Aggarwal Jul 20 '20 at 12:41
0

setState is an asynchronous function and it needs some time to update the state. It is not instantaneous. This is because setState alters the state and causes rerendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive. Thus the setState calls are asynchronous as well as batched for better UI experience and performance.

Fortunately, setState takes a callback:

this.setState({ param: "param" }, () => {                              
        //callback
        console.log(this.state.param) // your param
      });
Dani
  • 556
  • 7
  • 23