-1

I'm working on react table project, and can be searched and filtered by name and email both. The problem is Whenever I input a value it gets the previous state first, not the updated real-time value. Why is it and how to solve that? Thank You!

const URL = "https://jsonplaceholder.typicode.com/users";

function App() {

const [data, setData] = useState([]);
const [searchInput, setSearchInput] = useState("");
const [filteredResults, setFilteredResults] = useState([]);
  
  const getData = async () => {
    const response = await axios.get(URL);
    setData(response.data);
  };
  useEffect(() => {
    getData()
  }, []);

Filtering By Name

  const nameFilter = (e) => {
    if (e.target.value !== "") {
      setSearchInput(e.target.value);
      const results = data.filter((user) => {
        return user.name.toLowerCase().includes(searchInput.toLowerCase());
      });
      setFilteredResults(results);
    } else {
      setFilteredResults(data);
    }
  };

Filtering by Email

 const emailFilter = (e) => {
       if (e.target.value !== "") {
         setSearchInput(e.target.value);
         const results = data.filter((user) => {
           return user.email.toLowerCase().includes(searchInput.toLowerCase());
         });
         setFilteredResults(results);
       } else {
         setFilteredResults(data);
       }
     };

Table Header

 const renderHeader = () => {
      let headerElement = ["id", "name", "email", "Company Name", "Zipcode"];

      return headerElement.map((key, index) => {
        return <th key={index}>{key.toUpperCase()}</th>;
      });
    };

Table Body

const renderBody = () => {
  
  return (
    <>{
      searchInput ? (
        filteredResults.map(({ id, name, email, company, address }) => {
          return (
            <tr key={id}>
              <td>{id}</td>
              <td>{name}</td>
              <td>{email}</td>
              <td>{company.name}</td>
              <td>{address.zipcode}</td>
            </tr>
          );
        })
      ) :
        data.map(({ id, name, email, company, address }) => {
          return (
            <tr key={id}>
              <td>{id}</td>
              <td>{name}</td>
              <td>{email}</td>
              <td>{company.name}</td>
              <td>{address.zipcode}</td>
            </tr>
          );
        })}
    </>
    
  );
};

  
  return (
    <>
      <h1 id="title">React Table</h1>
      {!data.length ? (
        <h2>Loading...</h2>
      ) : (
        <div>
          <span>
            <input
              type="search"
              onChange={nameFilter}
              className="input"
              placeholder="Search by Name"
            />
            <input
              type="search"
              onChange={emailFilter}
              className="input"
              placeholder="Search by Email"
            />
          </span>
          <table id="employee">
            <thead>
              <tr>{renderHeader()}</tr>
            </thead>
            <tbody>{renderBody()}</tbody>
          </table>
        </div>
      )}
    </>
  );
}

export default App;
  • Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – Konrad Oct 02 '22 at 21:52

2 Answers2

0

This is because you set your filter state and after that immediately read its value (where the state isn't updated yet). To possible fixes:

  1. Use event.target.value as value to filter with
  2. Don't use a state value for the filtered results and instead use a "normal" variable (which gets recalculated every render, which occurs after every state change) and the value of this is the combined filtered result of both filters applied. Additionally you need a state to store the current input for the email and the name filter.
Fatorice
  • 515
  • 3
  • 12
0

That's because the setState hook is asynchronous event For that you have two solution you can use useEffect hook or use the callback syntax of useState

setFilteredResults(filteredResults=> [...filteredResults , results]);

Also as suggestion you use e.target.value directly in the includes method

Salem_Raouf
  • 400
  • 5
  • 10