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>
);
}
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.
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")
);