'redux useselector return undefined when i tried to find a value from redux state array according to useParams id

CodeSandbox link

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
);

Edit redux-useselector-return-undefined-when-i-tried-to-find-a-value-from-redux-state

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