3

I did a little bit of research but I found old examples with usually Redux or with one main component and cannot figure out the logic.

I can get data from an API but I do need to search, sort, filter, etc on that data (even at the same time). I know that I should not mutate the state directly, so in this case, I will have a couple of different state sets such as filteredData, sortedData, etc. However, which state am I going to pass to the Player component? How will sorting functionality will work on filtered data? I am extremely confused.

I would like to put my state and state-related logic in a Context but if this seems problematic or wrong, then I can try your approach.

Context

const ContextProvider = ({ children }) => {

  const [data, setData] = useState([]);
  const [filteredData, setFilteredData] = useState([]);
  const [sortedData, setSortedData] = useState([]);

  useEffect(() => {
    fetch('URL HERE')
      .then(data => data.json())
      .then(data=> setData(data));
  }, []);

  const handleSearch = e => {
    let value = e.target.value;
    const filteredData = data.filter(player=>
        player.name.includes(value)
    );
    setFilteredData(filteredData);
  };

  const handleSort = e => {
    let value = e.target.value;
    // I am trying to sort FILTERED DATA?
    const sortedData = filteredData.sort((a, b) => {
      if (a[value] < b[value]) {
        return 1;
      } else {
        return -1;
      }
    });
    setSortedData(sortedData);
  };

  return (
    <Context.Provider
      value={{ data, handleSearch, handleSort, filteredData, sortedData}}
    >
      {children}
    </Context.Provider>
  );
};

Search

const Search = () => {

  const { handleSearch, handleSort } = useContext(Context);
  return (
    <div className='row'>
      <Input
        type='search'
        name='search'
        onChange={handleSearch}
      ></Input>
      <Input
        type='select'
        name='select'
        onChange={handleSort}
      >
        <option value='name'>Name</option>
        <option value='team'>Team</option>
      </Input>
    </div>
  );
};

Player

const Player = () => {
  const {data, filteredData, sortedData} = useContext(Context);
  return (
      <ul>
        //{data or filteredData or sortedData will be mapped?}
        <li>// not sure what to do</li>
      </ul>
  );
};

I really appreciate code examples with some explanation.

3
  • or you could just introduce a new state which by default gonna have the data, const [finalData, setFInalData] = useState(data). And if you got sortedData or filteredData wanna display afterwards, just update the setFinalData(toWhatDataSetYouWant) Commented Nov 10, 2020 at 4:57
  • Update: I am waiting for a simple solution without using Context API at all. Commented Nov 10, 2020 at 7:24
  • I just made a working sandbox. Hope it helps. Commented Nov 10, 2020 at 10:22

1 Answer 1

1

There are many ways you can do this.

My suggestion would be:-

  1. Set const [filteredData, setFilteredData] = useState(data) (this will not work). So do useEffect() to update filteredData in Player then:-
useEffect(() => {
  setFilteredData(data)
}, [])
  1. On every request (inputs) made by user will be updated by setFilteredData()
  • if user do nothing with the inputs, then return filteredData which contain dafault data
  • if user do search input, then do filtering search on filteredData (using setFilteredData())
  • if user do sort input, then do sorting on filteredData (using setFilteredData())
  • if user do both search and sort inputs, then do both action on filteredData (using setFilteredData())
  1. in Player, you can just return filteredData

Context

const ContextProvider = ({ children }) => {

  const [data, setData] = useState([]);
  const [filteredData, setFilteredData] = useState([]);
  const [searchKey, setSearchKey] = useState('');
  const [sortKey, setSortKey] = useState([]);

  useEffect(() => {
    fetch('URL HERE')
      .then(data => data.json())
      .then(data=> setData(data));
  }, []);

  const handleSearch = () => {
    const filteredData = filteredData.filter(player=>
        player.name.includes(searchKey)
    );
    setFilteredData(filteredData);
  };

  const handleSort = () => {
    const sortedData = filteredData.sort((a, b) => {
      if (a[sortKey] < b[sortKey]) {
        return 1;
      } else {
        return -1;
      }
    });
    setFilteredData(sortedData);
  };

  return (
    <Context.Provider
      value={{
        data, filteredData, searchKey, sortKey,
        setFilteredData, setSearchKey, setSortKey,
        handleSearch, 
        handleSort, 
      }}
    >
      {children}
    </Context.Provider>
  );
};

Search

const Search = () => {

  const {
    searchKey, setSearchKey,
    sortKey, setSortKey
  } = useContext(Context);
  
  return (
    <div className='row'>
      <Input
        type='search'
        name='search'
        value={searchKey}
        onChange={e => setSearchKey(e.target.value)}
      ></Input>
      <Input
        type='select'
        name='select'
        value={sortKey}
        onChange={e => setSortKey(e.target.value)}
      >
        <option value='name'>Name</option>
        <option value='team'>Team</option>
      </Input>
    </div>
  );
};

Player

const Player = () => {
  const {
    data,
    filteredData, setFilteredData,
    searchKey, sortKey.
    handleSearch, handleSort
  } = useContext(Context);
 
  // set filteredData
  useEffect(() => {
    setFIlteredData(data)
  }, []) // should run once on mounted

  // do search
  // should be dependent on search input state (so you need a state to hold value need for searching)
  useEffect(() => {
    handleSearch()
  }, [searchKey]) // will only fired/run if searchState changes

  // do sorting
  // should be dependent on sorting input state (so you need a state to hold value need for sorting)
  useEffect(() => {
    handleSort()
  }, [sortKey]) // will only fired/run if sortingState changes

  return (
      <ul>
        {filteredData.map(data => (
          {/* display data info */}
        ))}
      </ul>
  );
};
Sign up to request clarification or add additional context in comments.

8 Comments

First of all, filteredData returns an empty array after the component is mounted. If I do search for something first, then I will be able to see players on the screen Also, sorting logic seems not working in this approach.
I already update my answer above. By the way, handling searching and sorting should be more accurate and makes sense if you handle it using useEffect instead of just directly do it by calling function handleSearch or handleSort
You can see above example to get an idea of how to make this work
You are calling setFilteredData without referencing it from then Context. Even if you reference it, this is not going to work.
It does not work. Still, there are problems with searching and sorting. Let's forget sorting. This logic somehow does not filter anything, it does always render the original state. I don't know why this simple thing became very problematic and complicated.
|

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.