我正在构建一个 React 应用程序,其中使用 Redux 进行状态管理,并使用 Express.js 后端处理 API 请求。我正在尝试实现搜索功能,以根据用户输入的搜索条件获取帖子。但是,我遇到了没有获取帖子的问题。它返回此 json 数据,而不是具有指定搜索查询的帖子
const useQuery = () => {
return new URLSearchParams(useLocation().search)
}
export default function Home() {
const classes = useStyles()
const dispatch = useDispatch();
const [search, setSearch] = useState('')
const [tags, setTag] = useState([])
const [currentId, setCurrentId] = useState(null)
const liked = useSelector((state) => state.posts.isLiked)
const del = useSelector((state) => state.posts.isDeleted)
const query = useQuery()
const navigate = useNavigate()
const page = query.get('page') || 1
const searchQuery = query.get('searchQuery')
useEffect(() => {
console.log('Effect triggered');
dispatch(getPosts())
}, [currentId, dispatch, liked, del])
console.log(search)
const searchingPost = () => {
if (search.trim() || tags.length > 0) {
dispatch(searchPost({ search, tags: tags.join(',') }))
console.log("this is in in the search",search)
} else {
navigate('/')
}
}
const handleKeyPress = (e) => {
console.log("outside the if condition")
if (e.keyCode === 'Enter') {
searchingPost()
console.log('inside the handlekeypress')
}
}
const handleChange = (newChips) => {
setTag(newChips)
}
const handleAdd = (tag) => {
setTag([...tags, tag])
}
const handleDelete = (tagToDelete) => {
setTag(tags.filter((tag) => tag !== tagToDelete))
}
return (
<>
<Grow in>
<Container maxWidth='xl'>
<Grid container justify='space-between' alignItems='stretch' spacing={3} className={classes.gridContainer}>
<Grid item xs={12} sm={6} md={9} >
<Posts currentId={currentId} setCurrentId={setCurrentId} />
</Grid>
<Grid item xs={12} sm={6} md={3} >
<AppBar className={classes.appBar} position='static' color='inherit'>
<TextField
name='search'
label='Search Memories'
fullWidth
value={search}
onChange={(e) => setSearch(e.target.value)}
onKeyPress={handleKeyPress}
variant='outlined'
/>
<MuiChipsInput
style={{ margin: '10px 0' }}
value={tags}
onChange={handleChange}
// onAdd = {handleAdd}
// onDelete = {handleDelete}
label='Search Tags'
variant='outlined'
></MuiChipsInput>
<Button onClick={searchingPost} className={classes.searchButton} color='primary'>Search</Button>
</AppBar>
<Form currentId={currentId} setCurrentId={setCurrentId} />
<br />
<Paper className={classes.pagination} elevation={6}>
<Paginate />
</Paper>
</Grid>
</Grid>
</Container>
</Grow>
</>
)
}
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { fetchPostsBySearch ,fetchPosts, createPost as apiCreatePost, updatePost as apiUpdatePost, likePost, deletePost } from '../api/index.js';
const initialState = {
isDeleted: false,
posts: [],
searchResults : [],
isLiked: false
}
const postSlice = createSlice({
name: 'posts',
initialState,
reducers: {
setPosts(state, action) {
state.posts = action.payload;
},
searchPost(state,action) {
state.searchResults = action.payload;
},
addPost(state, action) {
state.posts = [...state.posts, action.payload]
},
update(state, action) {
const updatedPost = action.payload;
// Find the post in the state by ID and update it
state.posts = state.posts.map(post => (post._id === updatedPost._id ? updatedPost : post));
// if post id equal to updated post id return updatedpost else return the previous post
},
like(state, action) {
const postliked = action.payload;
// Find the post in the state by ID and update it
state.posts = state.posts.map(post => (post._id === postliked._id ? postliked : post));
// state.posts = [...state.posts];
state.isLiked = true;
},
deletepost(state, action) {
console.log(action.payload)
state.posts = state.posts.filter((post) => post._id !== action.payload)
console.log('inside the reducer',state.posts)
// state.posts = [...state.posts];
state.isDeleted = true
},
resetFlags(state) {
state.isDeleted = false;
state.isLiked = false;
}
},
});
export const { setPosts, addPost, update, like, deletepost,resetFlags,searchPost } = postSlice.actions;
export default postSlice.reducer;
export const getPosts = createAsyncThunk('posts/getPosts', async (_, { dispatch }) => {
try {
const { data } = await fetchPosts();
dispatch(setPosts(data));
} catch (error) {
console.log(error.message);
}
});
export const searchPosts = createAsyncThunk('posts/searchPosts', async ({searchQuery}, { dispatch }) => {
try {
const { data } = await fetchPostsBySearch(searchQuery);
dispatch(searchPost(data));
console.log(data)
} catch (error) {
console.log(error.message);
}
});
// api index.js
export const fetchPostsBySearch = (searchQuery) => API.get(`/posts/search?searchQuery=${searchQuery.search || 'none'}&tags=${searchQuery.tags}`)
查询实际上已到达后端,但请求我从后端获取它 '{搜索:'Trek',标签:''} [[原型]] '
export const getPostBySearch = async (req,res) => {
const {searchQuery,tags} = req.query
console.log(req.body)
try {
console.log(searchQuery)
const title = new RegExp(searchQuery,'i') // i stands for ignore case
const posts = await PostMessage.find({ $or: [{title}, {tags : { $in: tags.split(',')}}]} )
res.json({data : posts})
console.log("this is for searchings posts",posts)
} catch(error) {
res.status(404).json({message : error.message })
}
}
如何解决此错误我想根据标题获取帖子,但响应没有返回任何内容,并且没有显示任何错误。
您有两个名称非常相似的操作,
searchPost
和 searchPosts
。 searchPost
是调度以更新状态的操作,以获取的“post(s)”作为操作负载,searchPosts
是用于发起网络请求的 Thunk 操作。 UI 代码正在调度前者而不是后者,因此 state.searchResults
被设置为 { search, tags: tags.join(',') }
中调度的 searchPost
操作中的 searchingPost
的值。
const searchingPost = () => {
if (search.trim() || tags.length > 0) {
dispatch(searchPost({ search, tags: tags.join(',') }));
console.log("this is in in the search", search);
} else {
navigate('/');
}
};
searchPost(state, action) {
state.searchResults = action.payload;
},
searchingPost
应改为调度 searchPosts
Thunk 操作。
const searchingPost = () => {
if (search.trim() || tags.length > 0) {
dispatch(searchPosts({ search, tags: tags.join(',') }));
} else {
navigate('/');
}
};
您也没有很好地利用 Redux-Toolkit。 Thunk 动作创建器实际上生成 3 个独立的动作:
.pending
、.fulfilled
和 .rejected
。将这些添加到状态切片的 extraReducers
,而不是手动创建其他操作。
示例:
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import {
fetchPostsBySearch,
fetchPosts,
createPost as apiCreatePost,
updatePost as apiUpdatePost,
likePost,
deletePost
} from '../api/index.js';
const initialState = {
isDeleted: false,
posts: [],
searchResults : [],
isLiked: false
};
export const getPosts = createAsyncThunk(
'posts/getPosts',
async (_, { rejectWithValue }) => {
try {
const { data } = await fetchPosts();
return data; // <-- return fulfilled data
} catch (error) {
console.log(error.message);
return rejectWithValue(error); // <-- return rejected value
}
},
);
export const searchPosts = createAsyncThunk(
'posts/searchPosts',
async ({ searchQuery }, { rejectWithValue }) => {
try {
const { data } = await fetchPostsBySearch(searchQuery);
return data; // return fulfilled value
} catch (error) {
console.log(error.message);
return rejectWithValue(error); // <-- return rejected value
}
}
);
const postSlice = createSlice({
name: 'posts',
initialState,
reducers: {
addPost(state, action) {
state.posts = [...state.posts, action.payload]
},
update(state, action) {
const updatedPost = action.payload;
state.posts = state.posts.map(post => (post._id === updatedPost._id ? updatedPost : post));
},
like(state, action) {
const postliked = action.payload;
state.posts = state.posts.map(post => (post._id === postliked._id ? postliked : post));
state.isLiked = true;
},
deletePost(state, action) {
state.posts = state.posts.filter((post) => post._id !== action.payload)
state.isDeleted = true
},
resetFlags(state) {
state.isDeleted = false;
state.isLiked = false;
}
},
extraReducers: builder => {
builder
.addCase(getPosts.fulfilled, (state, action) => {
state.posts = action.payload;
})
.addCase(getPosts.rejected, (state, action) => {
// store error for display, etc
})
.addCase(searchPosts.fulfilled, (state, action) => {
state.searchResults = action.payload;
})
.addCase(searchPosts.fulfilled, (state, action) => {
// store error for display, etc
});
},
});
export const {
addPost,
update,
like,
deletePost,
resetFlags
} = postSlice.actions;
export default postSlice.reducer;