我正在构建一个小型博客应用程序来帮助学习 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 开发工具中的组件。
问题解决了!这是浏览器扩展不兼容。禁用了我的广告拦截器,现在它就像预期的那样工作了。