Redux 工具包 - 为什么 useSelector 在尝试按 ID 检索帖子时返回未定义?

问题描述 投票:0回答:1

我正在构建一个小型博客应用程序来帮助学习 Redux Toolkit。我遇到一个问题,在 postsSlice 中定义并导入 SinglePostView 的选择器在尝试使用 useParams() 按 ID 检索帖子时返回未定义的值。其他选择器在同一组件中工作,但我很难弄清楚为什么这个选择器只会返回未定义的值。关于这里出了什么问题或如何解决它有什么想法吗?预先感谢您提供任何建设性意见!

main.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';
import { store } from './app/store.js';
import { Provider } from 'react-redux';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { fetchUsers } from './features/users/usersSlice'
import './index.css';

// Immediately fetch users when app loads
store.dispatch(fetchUsers());

ReactDOM.createRoot(document.getElementById('root')).render(
    <Provider store={store}>
      <BrowserRouter>
        <Routes>
          <Route path="/*" element={<App />} />
        </Routes>
      </BrowserRouter>
    </Provider>
);

应用程序.jsx

import { Routes, Route } from 'react-router-dom';
import Layout from './components/Layout';
import AddPostView from "./features/posts/AddPostView";
import PostsView from "./features/posts/postsView";
import SinglePostView from './features/posts/SinglePostView';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<PostsView />} />
        <Route path="post">
          <Route index element={<AddPostView />} />
          <Route path=":postId" element={<SinglePostView />} />
        </Route>
      </Route>
    </Routes>
  );
}

export default App;

store.js

import { configureStore } from '@reduxjs/toolkit';
import postsReducer from '../features/posts/postsSlice';
import usersReducer from '../features/users/usersSlice';

export const store = configureStore({
  reducer: {
    posts: postsReducer,
    users: usersReducer
  }
});

postsSlice.js

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { sub } from 'date-fns';
import axios from 'axios';
const POSTS_URL = 'https://jsonplaceholder.typicode.com/posts';

const initialState = {
  posts: [],
  status: 'idle',
  error: null
};

export const fetchPosts = createAsyncThunk('posts/fetchPosts', () => (
  axios.get(POSTS_URL)
    .then((response) => response.data)
));

export const addPost = createAsyncThunk('posts/addPost', (newPost) => (
  axios.post(POSTS_URL, newPost)
    .then((response) => response.data)
));

const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    reactionAdded(state, action) {
      const {postId, reaction} = action.payload;
      const post = state.posts.find(post => post.id === postId);
      if (post) {
        post.reactions[reaction]++;
      }
    },
    reactionRemoved(state, action) {
      const {postId, reaction} = action.payload;
      const post = state.posts.find(post => post.id === postId);
      if (post && post.reactions[reaction] > 0) {
        post.reactions[reaction]--;
      }
    }
  },
  extraReducers(builder) {
    builder
      .addCase(fetchPosts.pending, (state) => {
        state.status = 'pending';
      })
      .addCase(fetchPosts.fulfilled, (state, action) => {
        state.status = 'fulfilled';
        const posts = action.payload.map(post => {
          post.createdAt = new Date().toISOString();
          post.reactions = {
            thumbsUp: 0,
            wow: 0,
            heart: 0,
            rocket: 0,
            coffee: 0
          }
          return post;
        });
        state.posts = state.posts.concat(posts);
      })
      .addCase(fetchPosts.rejected, (state, action) => {
        state.status = 'rejected';
        state.error = action.error.message;
      })
      .addCase(addPost.fulfilled, (state, action) => {
        action.payload.id = state.posts.length + 1;
        action.payload.userId = Number(action.payload.userId);
        action.payload.createdAt = new Date().toISOString();
        action.payload.reactions = {
          thumbsUp: 0,
          wow: 0,
          heart: 0,
          rocket: 0,
          coffee: 0
        }
        state.posts.push(action.payload);
      });
  } 
});

export const selectAllPosts = (state) => state.posts.posts;

export const selectPostById = (state, postId) => 
  state.posts.posts.find((post) => post.id === postId);

export const getPostsStatus = (state) => state.posts.status;
export const getPostsError = (state) => state.posts.error;
export const { postAdded, reactionAdded, reactionRemoved } = postsSlice.actions;
export default postsSlice.reducer;

SinglePostView.jsx

import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { selectPostById } from './postsSlice';
import PostAuthorView from './PostAuthorView';
import CreatedAt from './CreatedAtView';
import ReactionsView from './ReactionsView';

const SinglePostView = () => {

  const { postId } = useParams();

  // Returns undefined
  const post = useSelector((state) => selectPostById(state, Number(postId)))

  if (!post) {
    return (
      <section>
        <h2>This post doesn't exist!</h2>
      </section>
    );
  }
  return (
    <article>
      <h2>{post.title}</h2>
      <p>{post.body}</p>
      <p className="postCredit">
        <PostAuthorView userId={post.userId} />
        <CreatedAt timestamp={post.createdAt} />
      </p>
      <ReactionsView post={post} />
    </article>
  )
}
export default SinglePostView;

我尝试将选择器定义中的严格相等比较更改为松散比较,我已将其他选择器导入并使用到同一组件中(它们都按预期工作,除了 selectPostById),并且我尝试检查React 和 Redux 开发工具中的组件。

reactjs redux react-redux redux-toolkit
1个回答
0
投票

问题解决了!这是浏览器扩展不兼容。禁用了我的广告拦截器,现在它就像预期的那样工作了。

© www.soinside.com 2019 - 2024. All rights reserved.