Redux useSelect 没有按预期更新
Redux useSelect doesn't get updated as expected
我正在做一个小的概念验证项目,使用 React 和 Redux,以及 useSelector 和 useDispatch 挂钩。我正在尝试异步获取一些数据,为此我使用了 thunk。我想我在概念上遗漏了一些东西。即使我的状态按预期工作,我也无法使用 useSelector 从 api 获取数据。
这是代码。我的行动:
import axios from "axios";
export const API_FETCH_POSTS = 'API_FETCH_POSTS';
export const fetchPosts = (postId) => { // to simulate post request
return (dispatch) => {
let baseUrl = 'https://jsonplaceholder.typicode.com/';
let postFix = 'comments?postId=';
let url = `${baseUrl}${postFix}${postId}`;
axios.get(url)
.then(response => {
const data = response.data;
console.log(JSON.stringify(data)); // work!
dispatch(fetchPostsSuccess(data));
});
}
};
const fetchPostsSuccess = posts => {
return {
type: API_FETCH_POSTS,
payload: posts
}
};
我的减速器:
import {API_FETCH_POSTS} from "./apiActions";
const initialState = {
getPostsReq : {
posts: [],
}
};
const apiReducer = (state = initialState, action) => {
let getPostsReq;
switch (action.type) {
case API_FETCH_POSTS:
getPostsReq = {
posts: [...state.getPostsReq.posts]
};
return {
...state,
getPostsReq
};
default: return state;
}
};
export default apiReducer;
和rootReducer:
import {combineReducers} from 'redux';
import apiReducer from "./api/apiReducer";
export default combineReducers({
api: apiReducer
})
并存储:
const initialState = {};
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(
applyMiddleware(thunk)
)
);
export default store;
我的 React 组件有问题:
function PostContainer(props) {
const posts = useSelector(state => state.api.getPostsReq.posts);
const dispatch = useDispatch();
const logPosts = () => {
{/*why doesn't this line work???*/}
console.log(JSON.stringify(posts));
}
return (
<div>
<button onClick={() => {
dispatch(fetchPosts(1));
logPosts();
}}>Fetch Posts</button>
<div>
{/*why doesn't this line work???*/}
{posts.map(post => <p>{post.body}</p>)}
</div>
</div>
);
}
export default PostContainer;
我希望在我按下按钮后,函数 fetchPosts
被调度,因为我使用 thunk,所以我不应该有任何异步问题。但由于某种原因,我无法使用 useSelector()
挂钩获取我的状态。我既不能渲染状态,也不能在控制台中记录它。
我在这里错过了什么?
如果更方便的话,这里是完整的代码 - https://github.com/JavavaJ/use-select-problem
问题:不存储帖子
你的选择器没问题,问题出在你的减速器上!您发送了一个操作,该操作在 payload
:
中有 array
个帖子
const fetchPostsSuccess = posts => {
return {
type: API_FETCH_POSTS,
payload: posts
}
};
但是当您在 reducer 中响应此操作时,您会完全忽略 payload
而只是 return 您已经拥有的相同帖子:
const apiReducer = (state = initialState, action) => {
let getPostsReq;
switch (action.type) {
case API_FETCH_POSTS:
getPostsReq = {
posts: [...state.getPostsReq.posts]
};
return {
...state,
getPostsReq
};
default: return state;
}
};
解决方案:从操作中添加帖子
您可以像这样重写您的减速器以使用 Redux immutable update patterns 添加帖子。
const apiReducer = (state = initialState, action) => {
switch (action.type) {
case API_FETCH_POSTS:
return {
...state,
getPostsReq: {
...state.getPostsReq,
posts: [...state.getPostsReq.posts, ...action.payload]
}
};
default:
return state;
}
};
使用Redux Toolkit就简单多了!使用该工具包,您可以“改变”减速器中的草稿状态,因此我们不需要复制所有内容。
const apiReducer = createReducer(initialState, {
[API_FETCH_POSTS]: (state, action) => {
// use ... to push individual items separately
state.getPostsReq.posts.push(...action.payload);
}
});
我正在做一个小的概念验证项目,使用 React 和 Redux,以及 useSelector 和 useDispatch 挂钩。我正在尝试异步获取一些数据,为此我使用了 thunk。我想我在概念上遗漏了一些东西。即使我的状态按预期工作,我也无法使用 useSelector 从 api 获取数据。
这是代码。我的行动:
import axios from "axios";
export const API_FETCH_POSTS = 'API_FETCH_POSTS';
export const fetchPosts = (postId) => { // to simulate post request
return (dispatch) => {
let baseUrl = 'https://jsonplaceholder.typicode.com/';
let postFix = 'comments?postId=';
let url = `${baseUrl}${postFix}${postId}`;
axios.get(url)
.then(response => {
const data = response.data;
console.log(JSON.stringify(data)); // work!
dispatch(fetchPostsSuccess(data));
});
}
};
const fetchPostsSuccess = posts => {
return {
type: API_FETCH_POSTS,
payload: posts
}
};
我的减速器:
import {API_FETCH_POSTS} from "./apiActions";
const initialState = {
getPostsReq : {
posts: [],
}
};
const apiReducer = (state = initialState, action) => {
let getPostsReq;
switch (action.type) {
case API_FETCH_POSTS:
getPostsReq = {
posts: [...state.getPostsReq.posts]
};
return {
...state,
getPostsReq
};
default: return state;
}
};
export default apiReducer;
和rootReducer:
import {combineReducers} from 'redux';
import apiReducer from "./api/apiReducer";
export default combineReducers({
api: apiReducer
})
并存储:
const initialState = {};
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(
applyMiddleware(thunk)
)
);
export default store;
我的 React 组件有问题:
function PostContainer(props) {
const posts = useSelector(state => state.api.getPostsReq.posts);
const dispatch = useDispatch();
const logPosts = () => {
{/*why doesn't this line work???*/}
console.log(JSON.stringify(posts));
}
return (
<div>
<button onClick={() => {
dispatch(fetchPosts(1));
logPosts();
}}>Fetch Posts</button>
<div>
{/*why doesn't this line work???*/}
{posts.map(post => <p>{post.body}</p>)}
</div>
</div>
);
}
export default PostContainer;
我希望在我按下按钮后,函数 fetchPosts
被调度,因为我使用 thunk,所以我不应该有任何异步问题。但由于某种原因,我无法使用 useSelector()
挂钩获取我的状态。我既不能渲染状态,也不能在控制台中记录它。
我在这里错过了什么?
如果更方便的话,这里是完整的代码 - https://github.com/JavavaJ/use-select-problem
问题:不存储帖子
你的选择器没问题,问题出在你的减速器上!您发送了一个操作,该操作在 payload
:
array
个帖子
const fetchPostsSuccess = posts => {
return {
type: API_FETCH_POSTS,
payload: posts
}
};
但是当您在 reducer 中响应此操作时,您会完全忽略 payload
而只是 return 您已经拥有的相同帖子:
const apiReducer = (state = initialState, action) => {
let getPostsReq;
switch (action.type) {
case API_FETCH_POSTS:
getPostsReq = {
posts: [...state.getPostsReq.posts]
};
return {
...state,
getPostsReq
};
default: return state;
}
};
解决方案:从操作中添加帖子
您可以像这样重写您的减速器以使用 Redux immutable update patterns 添加帖子。
const apiReducer = (state = initialState, action) => {
switch (action.type) {
case API_FETCH_POSTS:
return {
...state,
getPostsReq: {
...state.getPostsReq,
posts: [...state.getPostsReq.posts, ...action.payload]
}
};
default:
return state;
}
};
使用Redux Toolkit就简单多了!使用该工具包,您可以“改变”减速器中的草稿状态,因此我们不需要复制所有内容。
const apiReducer = createReducer(initialState, {
[API_FETCH_POSTS]: (state, action) => {
// use ... to push individual items separately
state.getPostsReq.posts.push(...action.payload);
}
});