'redux useselector return undefined when i tried to find a value from redux state array according to useParams id
i'm trying to create a demo add post app and it have PostsList.jsx and SinglePostPage.jsx components. I am using redux-toolkit, axios, react-router-dom@6 and JSONplaceholder in this app. Every post section have a View Post Link button to see the full post.
PostList.jsx
const PostsList = () => {
const posts = useSelector((state) => state.posts.entities);
const renderedPosts = posts.map((post) => (
<article key={post.id}>
<h3>{post.title}</h3>
<p> {post.body.substring(0, 100)} </p>
<Link to={`posts/${post.id}`}> View Post </Link>
</article>
));
return (
<section>
<h2>Posts</h2>
{renderedPosts}
</section>
);
};
My PostSlice.js File:
const initialState = {
entities: [],
status: 'idle',
error: null,
};
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
return response.data;
});
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
//omit the reducer functions
},
extraReducers(builder) {
builder
.addCase(fetchPosts.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = 'succeeded';
state.entities = state.entities.concat(action.payload);
})
.addCase(fetchPosts.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
},
});
I face a problem in SinglePostPage.jsx components.The PostSlist components work properly . But useSelector return undefind when i am trying to get value form redux state array accoring to the id give by useParams hook.
SinglePostPage.jsx
const SinglePostPage = () => {
const { postId } = useParams();
console.log('postId:', postId);
// postId: 1
const post = useSelector((state) => state.posts.entities.find(post=> post.id === postId));
console.log('post:', post);
// post: undefined
if (!post) {
return (
<div>
<BtnLink to="/">Posts</BtnLink>
<h1>
The post doesn't found. Please, go back to the posts section.
</h1>
</div>
);
}
return (
<div>
<div className="flex justify-between mb-7">
<BtnLink to="/">Posts</BtnLink>
<BtnLink to={`/editPost/${postId}`}>Edit Post</BtnLink>
</div>
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
</div>
);
};
export default SinglePostPage;
Instead of postId, if i use a integer then i get the value of specific id
const post = useSelector((state) => state.posts.entities.find(post=> post.id === 1));
console.log('post:', post);
// post: {data}
how can i solve this problem...????
Solution 1:[1]
The issue is that the route params are always strings and the post id property is a number.
{ "userId": 1, "id": 1, // <-- number type "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" },
This is why when using a number directly to match (state.posts.entities.find(post=> post.id === 1)) it worked for you.
You are using strict (===) equality when searching for a matching post by id.
const { postId } = useParams();
const post = useSelector((state) =>
state.posts.entities.find((post) => post.id === postId)
);
post.id is a number and postId is a string, so the equality check returns false because the type is not the same.
To resolve, you could either use loose equality so a type conversion will be done:
const { postId } = useParams();
const post = useSelector((state) =>
state.posts.entities.find((post) => post.id == postId) // 1 == "1" -> true
);
Or better to convert the post id to a string and keep the strict equality:
const { postId } = useParams();
const post = useSelector((state) =>
state.posts.entities.find((post) => String(post.id) === postId) // "1" === "1" -> true
);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
