4

Hello I have created a search bar with a multipl filter, it works but the functions are too dependent on each other. The problem here is that the functions are handling multiple cases. would it be possible to lighten each function by chaining them and how ? I don't really get chaining method. thanks

import React, { useState, useEffect } from "react";
import Search from "./Search";
import Anime from "./Anime";
import "./App.css";

const KIJAN_API_URL = "https://api.jikan.moe/v3/top/anime/1/upcoming";
const App = () => {
  const [animes, setAnimes] = useState([]);
  const [sortedAnimes, setSortedAnimes] = useState([]);
  const [searchValue, setSearchValue] = useState("")
  const [filterByType, setFilterByType] = useState("");
  const [filterByYear, setFilterByYear] = useState("");

  useEffect(() => {
    fetch(KIJAN_API_URL)
      .then(response => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error("Something went wrong");
        }
      })
      .then(jsonResponse => {
        setAnimes(jsonResponse.top);
      })
      .catch(error => {
        console.log(error);
      });
  }, []);

  useEffect(() => {

    const callFilterByType = result => {
      if (filterByType === "") {
        callFilterByYear(result);
        console.log(result);
      } else {
        result = result.filter(anime => anime.type === filterByType);
        callFilterByYear(result);
        console.log(result);
      }
    };

    const callFilterByYear = result => {
      if (filterByYear === "") {
        setSortedAnimes(result);
      } else {
        const regex = new RegExp(`${filterByYear}`, "gi");
        result = result.filter(anime => regex.test(anime.start_date));
        setSortedAnimes(result);
        console.log(result);
      }
    };

    if (searchValue === "") {
      callFilterByType(animes);
    } else {
      const regex = new RegExp(`${searchValue}`, "gi");
      console.log("search : ", searchValue);
      const result = animes.filter(anime => regex.test(anime.title));
      callFilterByType(result);
      console.log(result);
    }


  }, [searchValue, animes, filterByType, filterByYear]);

  return (
    <div className="App">
      <Search
        searchValue={searchValue}
        setSearchValue={setSearchValue}
        filterByType={filterByType}
        setFilterByType={setFilterByType}
        filterByYear={filterByYear}
        setFilterByYear={setFilterByYear}
      />
      {sortedAnimes.length > 0 ? (
        sortedAnimes.map((anime, index) => {
          return <Anime key={index} anime={anime} />;
        })
      ) : (
        <span>Aucune correspondance</span>
      )}
    </div>
  );
};

export default App;
6
  • See this other question, simply separate each part of the logic into its own useEffect. Commented Sep 19, 2019 at 10:18
  • Hello @Alvaro thank you for the insight but i don't think that I can't manage data with 3 different useEffect even if they monitor different inputs. The main problem is that the functions must give a result by working together or working separately. If I work on the same array the UseEffect will set the value undefined and therefore I couldn't use them in other useEffectsthat have to work with this value Commented Sep 19, 2019 at 13:47
  • @ ArBabacar_ maybe refactoring the code will help. Make use of useState and useEffect. Its difficult to tell exactly how, as we dont know where do searchValue, animes, setSortedAnimes come from. Commented Sep 19, 2019 at 14:17
  • Oh Sorry @Alvaro searchValue is coming from my SearchBar component , animes is coming from an API and setSortedAnimes updated the array animes when you apply a filter Commented Sep 19, 2019 at 14:42
  • Could you share the full component? Im asking because Im not sure if setSortedAnimes belongs to a useState or how is it modifying the fetched animes. Commented Sep 19, 2019 at 14:49

2 Answers 2

6

SandBox Sample You can do first round of simplification like this:

useEffect(() => {
    let result = [...animes];

    if(searchValue) {
      const searchRegex = new RegExp(`${searchValue}`, "gi");
      result = result.filter(anime => searchRegex.test(anime.title));      
    }

    if(filterByType) {
      result = result.filter(anime => anime.type === filterByType);      
    }

    if(filterByYear) {
      const yearRegex = new RegExp(`${filterByYear}`, "gi");
      result = result.filter(anime => yearRegex.test(anime.start_date));
    }
    setSortedAnimes(result);

}, [searchValue, animes, filterByType, filterByYear]);

It can be reduced to more compact form, like:

useEffect(() => {
    const searchRegex = searchValue && new RegExp(`${searchValue}`, "gi");
    const yearRegex = filterByYear && new RegExp(`${filterByYear}`, "gi");
    const result = animes.filter(anime => 
      (!searchRegex || searchRegex.test(anime.title)) &&
      (!filterByType || anime.type === filterByType)) &&
      (!yearRegex || yearRegex.test(anime.start_date))
    )
    setSortedAnimes(result);
}, [searchValue, animes, filterByType, filterByYear]);

More idiomatic way would be use use momoisation hook. i.e. Remove sortedAnimes as state and

const sortedAnimes = useMemo(() => {
    const searchRegex = searchValue && new RegExp(`${searchValue}`, "gi");
    const yearRegex = filterByYear && new RegExp(`${filterByYear}`, "gi");
    return animes.filter(anime => 
      (!searchRegex || searchRegex.test(anime.title)) &&
      (!filterByType || anime.type === filterByType)) &&
      (!yearRegex || yearRegex.test(anime.start_date))
    )
}, [searchValue, animes, filterByType, filterByYear]);
Sign up to request clarification or add additional context in comments.

3 Comments

Above useMemo statement is to be used as replacement of [sortedAnimes, setSortedAnimes] = useState([]) statement after other state statement. With useMemo you will not need second effect (which as doing the filtering) in your original code.
If you can provide sample working code in some online sandbox, like codesandbox, codepen etc., it would be easy to check.
it's much cleaner know i will clean it even more and i will let you know thank you
0

try this

if you are using filter method inside jsx then you try this method.

Let me brief it, consider userInfo like an object containing fields like name, email, location etc. so, if you want your filter method to provide your search results based on these fields value then you can use something like this in jsx.

    {userInfo.filter((user) => (
                                    
                        user.name.toLowerCase().includes(cloneSearchTerm)
                        ||
                        user.email.toLowerCase().includes(cloneSearchTerm)
                        ||
                        user.location.toLowerCase().includes(cloneSearchTerm)
                    )
    
                    ).map((user, idx) => (
                        <div key={idx}>
                                <span>{user.name}</span>
                                <span>{user.email}</span>
                                <span>{user.location}</span>
                        </div>

                    ))}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.