Search code examples
reactjsfilterfrontendfetchmapping

How to filter an array with fetched data in React js?


I am trying to filter fetched blog posts by entering a user number in a input. This number should match the post userId and show only the posts 'written' by that given user (which is a number from 1 to 10 and each user have 10 posts).

Problem: When I enter a number in the input, posts disappear and doesn't filter anything. I am pretty sure than I am stuck with the filter function.

Thanks in advance!

function App(pst) {

  const [posts, setPosts] = useState([]);
  const [query, setQuery] = useState('');

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts/')
      .then(response => response.json())
      .then(json => setPosts(json))
  }, [])

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      
      <Router>
          <NavBar />
          <Switch>
            <Route exact path='/'>
              <Home />
              <SearchBar 
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              />
              <Grid container spacing={2} justify='center'>
                {posts.filter((val) => {
                  if (query === '') {
                    return val
                  } else if (query === val.userId) {
                    return val
                  }
                  console.log(query);
                }).map((pst) => (
                  <PostCard pst={pst} key={pst.id}/>
                ))}
              </Grid>
            </Route>
            <Route exact path='/singlePost/:postId' component={SinglePost} />
            <Route exact path='/PostList'>
              <PostList pst={pst}/>
            </Route>
          </Switch>
      </Router>
    </ ThemeProvider>
  );
}

Solution

  • There are two main ways of filtering data: client-side or server-side. Sometimes the server-side can be more performant when dealing with large data sets and complex data structures. In your example, the data is pretty small and the data structure is pretty flat so client-side filtering would be more than enough.

    First thing is first, since you want all the data to be present to the client to be filtered, you'll want to store that data to its own posts state. Then you'll have some sort of searchValue state that changes whenever the user changes the input value. Utilizing the two states, you can filter the posts with searchValue and store the result to its own filteredPosts state.

    React will only re-render the component when state or props change. Therefore, you'll want to utilize state changes to keep your component up-to-date.

    Working demo

    Edit Filter Data

    Code

    App.js

    import * as React from "react";
    import "./styles.css";
    
    export default function App() {
      const [searchValue, setSearch] = React.useState(0);
      const [posts, setPosts] = React.useState([]);
      const [filteredPosts, setFilteredPosts] = React.useState([]);
    
      const handleSearchChange = (event) => {
        /*
          This "handleSearchChange" is a callback function that accepts an
          "event" as an argument. 
    
          This "event" contains the "input" DOM element. And this
          element contains a "value" property that is stored as 
          a string on the "target" property. This "event.target.value"
          will update the input's "searchValue" state.
    
          Please note, "event.target.value" will be a string and when
          filtering the post's "id", which is a number, a string value of "1" 
          won't equal a post id of number 1! Therefore, parseInt(string, 10)
          coverts the string value to a number value.
        */
        setSearch(parseInt(event.target.value, 10));
      };
    
      const handleSearchReset = () => {
        setSearch(0);
      };
    
      React.useEffect(() => {
        fetch("https://jsonplaceholder.typicode.com/posts")
          .then((response) => response.json())
          .then((json) => setPosts(json));
      }, []);
    
      React.useEffect(() => {
        /*
          When the searchValue changes, filter the posts by id with
          searchValue and store the result to "filteredPosts"
        */
        setFilteredPosts(posts.filter((post) => post.id === searchValue));
      }, [posts, searchValue]);
    
      return (
        <div className="App">
          <h1>Search Posts</h1>
          <input
            min={0}
            value={searchValue}
            type="number"
            onChange={handleSearchChange}
          />
          <br />
          <button type="button" onClick={handleSearchReset}>
            Reset Search
          </button>
          <h1>Filtered Posts</h1>
          <pre className="code">
            <code>{JSON.stringify(filteredPosts, null, 2)}</code>
          </pre>
          <h1>Available Posts</h1>
          <pre className="code">
            <code>{JSON.stringify(posts, null, 2)}</code>
          </pre>
        </div>
      );
    }
    

    index.js

    import * as React from "react";
    import ReactDOM from "react-dom";
    import App from "./App";
    
    ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      document.getElementById("root")
    );