如何在另一个 asyncThunk 中调度 asyncThunk
how to dispatch asyncThunk inside another asyncThunk
我创建了两个异步 thunk:
export const foo = createAsyncThunk(
'slice/foo',
async ( ) => {
const res = await fetch('/')
return res.data
}
);
export const bar = createAsyncThunk(
'slice/bar',
async ( ) => {
const res = await foo()
return res.data
}
);
基本上我试图在 bar()
中调用 foo()
然后在 react 组件的 useEffect 中,我像这样调度 bar()
:
React.useEffect( async () => {
await dispatch( bar() );
}, [bar]);
foo
正在派遣,但 bar
未派遣。
我做错了什么?
提前致谢。
编辑1。它有点像反模式,但我基本上想做的是链接 bar() 和 foo(),我不知道任何其他(正确的?)方式
编辑2。基于@Nadia Chibrikova 的评论,这也是我使用 thunkAPI 尝试过的:
export const bar = createAsyncThunk(
'slice/bar',
async ( thunkApi ) => {
thunkApi .dispatch(foo())
}
);
但我收到以下错误:
Cannot read property 'dispatch' of undefined
链接 Thunks
这不是一个很好的设计模式,但可以完成。您的“Edit2”很接近,但 thunkApi
是有效负载创建者的 second 参数。第一个参数是当您将动作创建者称为 bar(args)
时传递的参数。如果您不需要参数,那么您可以使用 _
来表示该变量未被使用。
export const foo = createAsyncThunk(
"slice/foo",
async () => {
const res = await fetch("/");
return res.json();
}
);
export const bar = createAsyncThunk(
"slice/bar",
async (_, { dispatch }) => {
const fooAction = await dispatch(foo());
return fooAction.payload;
}
);
调用 dispatch(bar())
将按以下顺序发送操作:
- "slice/bar/pending"
- "slice/foo/pending"
- "slice/foo/fulfilled"
- "slice/bar/fulfilled"
单独的操作
正如@Marcus Melodious 所推荐的,最好在组件中拥有完全独立的操作和处理调度。如果 bar
需要来自 foo
的一些数据(比如 id
或 token
),那么它应该将该数据作为其动作创建者的参数。
这是一个示例,我们获取 post,然后根据 post 的 post 属性 获取用户配置文件。
export const fetchPost = createAsyncThunk(
"slice/fetchPost",
async (postId) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
return res.json();
}
);
export const fetchUser = createAsyncThunk(
"slice/fetchUser",
async (userId) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
return res.json();
}
);
您可以使用一个 useEffect
并查看调度结果
useEffect(() => {
const execute = async () => {
const postAction = await dispatch(fetchPost(POST_ID));
const {userId} = postAction.payload;
dispatch(fetchUser(userId));
};
execute();
}, []);
或者您可以从两个单独的 useEffect
挂钩发起两个请求。您的第二个钩子知道何时根据 useSelector
.
中 Redux 状态的变化进行分派
const Test = () => {
const dispatch = useDispatch();
const post = useSelector((state) => state.slice.post);
const user = useSelector((state) => state.slice.user);
const POST_ID = 1; // just a dummy constant for testing
console.log(post, user);
useEffect(() => {
const loadPost = async () => {
dispatch(fetchPost(POST_ID));
};
loadPost();
}, []); // run once on mount
useEffect(() => {
const loadUser = async (userId) => {
dispatch(fetchUser(userId));
};
// check if post has has been set and has a userId
if ( post && post.userId ) {
loadUser(post.userId);
}
}, [post]); // run when post changes
return <div/>;
};
这是两个钩子方法的完整代码示例:
import {
createAsyncThunk,
configureStore,
createSlice
} from "@reduxjs/toolkit";
import { Provider, useDispatch, useSelector } from "react-redux";
import React, { useEffect } from "react";
export const fetchPost = createAsyncThunk(
"slice/fetchPost",
async (postId) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
return res.json();
}
);
export const fetchUser = createAsyncThunk(
"slice/fetchUser",
async (userId) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
return res.json();
}
);
const slice = createSlice({
name: "slice",
initialState: {
user: undefined,
post: undefined
},
reducers: {},
extraReducers: {
[fetchPost.fulfilled]: (state, action) => {
state.post = action.payload;
},
[fetchUser.fulfilled]: (state, action) => {
state.user = action.payload;
}
}
});
const store = configureStore({ reducer: { slice: slice.reducer } });
const PostHeader = ({ postId }) => {
const dispatch = useDispatch();
const post = useSelector((state) => state.slice.post);
const user = useSelector((state) => state.slice.user);
console.log(post, user);
useEffect(() => {
const loadPost = async () => {
dispatch(fetchPost(postId));
};
loadPost();
}, [postId, dispatch]); // run once on mount
useEffect(() => {
const loadUser = async (userId) => {
dispatch(fetchUser(userId));
};
// check if post has has been set and has a userId
if (post && post.userId) {
loadUser(post.userId);
}
}, [post, dispatch]); // run when post changes
return (
<div>
{!!post && <h1>{post.title}</h1>}
{!!user && <h2>By {user.name}</h2>}
</div>
);
};
export default function App() {
return (
<Provider store={store}>
<PostHeader postId={1} />
</Provider>
);
}
我创建了两个异步 thunk:
export const foo = createAsyncThunk(
'slice/foo',
async ( ) => {
const res = await fetch('/')
return res.data
}
);
export const bar = createAsyncThunk(
'slice/bar',
async ( ) => {
const res = await foo()
return res.data
}
);
基本上我试图在 bar()
中调用 foo()
然后在 react 组件的 useEffect 中,我像这样调度 bar()
:
React.useEffect( async () => {
await dispatch( bar() );
}, [bar]);
foo
正在派遣,但 bar
未派遣。
我做错了什么?
提前致谢。
编辑1。它有点像反模式,但我基本上想做的是链接 bar() 和 foo(),我不知道任何其他(正确的?)方式
编辑2。基于@Nadia Chibrikova 的评论,这也是我使用 thunkAPI 尝试过的:
export const bar = createAsyncThunk(
'slice/bar',
async ( thunkApi ) => {
thunkApi .dispatch(foo())
}
);
但我收到以下错误:
Cannot read property 'dispatch' of undefined
链接 Thunks
这不是一个很好的设计模式,但可以完成。您的“Edit2”很接近,但 thunkApi
是有效负载创建者的 second 参数。第一个参数是当您将动作创建者称为 bar(args)
时传递的参数。如果您不需要参数,那么您可以使用 _
来表示该变量未被使用。
export const foo = createAsyncThunk(
"slice/foo",
async () => {
const res = await fetch("/");
return res.json();
}
);
export const bar = createAsyncThunk(
"slice/bar",
async (_, { dispatch }) => {
const fooAction = await dispatch(foo());
return fooAction.payload;
}
);
调用 dispatch(bar())
将按以下顺序发送操作:
- "slice/bar/pending"
- "slice/foo/pending"
- "slice/foo/fulfilled"
- "slice/bar/fulfilled"
单独的操作
正如@Marcus Melodious 所推荐的,最好在组件中拥有完全独立的操作和处理调度。如果 bar
需要来自 foo
的一些数据(比如 id
或 token
),那么它应该将该数据作为其动作创建者的参数。
这是一个示例,我们获取 post,然后根据 post 的 post 属性 获取用户配置文件。
export const fetchPost = createAsyncThunk(
"slice/fetchPost",
async (postId) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
return res.json();
}
);
export const fetchUser = createAsyncThunk(
"slice/fetchUser",
async (userId) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
return res.json();
}
);
您可以使用一个 useEffect
并查看调度结果
useEffect(() => {
const execute = async () => {
const postAction = await dispatch(fetchPost(POST_ID));
const {userId} = postAction.payload;
dispatch(fetchUser(userId));
};
execute();
}, []);
或者您可以从两个单独的 useEffect
挂钩发起两个请求。您的第二个钩子知道何时根据 useSelector
.
const Test = () => {
const dispatch = useDispatch();
const post = useSelector((state) => state.slice.post);
const user = useSelector((state) => state.slice.user);
const POST_ID = 1; // just a dummy constant for testing
console.log(post, user);
useEffect(() => {
const loadPost = async () => {
dispatch(fetchPost(POST_ID));
};
loadPost();
}, []); // run once on mount
useEffect(() => {
const loadUser = async (userId) => {
dispatch(fetchUser(userId));
};
// check if post has has been set and has a userId
if ( post && post.userId ) {
loadUser(post.userId);
}
}, [post]); // run when post changes
return <div/>;
};
这是两个钩子方法的完整代码示例:
import {
createAsyncThunk,
configureStore,
createSlice
} from "@reduxjs/toolkit";
import { Provider, useDispatch, useSelector } from "react-redux";
import React, { useEffect } from "react";
export const fetchPost = createAsyncThunk(
"slice/fetchPost",
async (postId) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
return res.json();
}
);
export const fetchUser = createAsyncThunk(
"slice/fetchUser",
async (userId) => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
return res.json();
}
);
const slice = createSlice({
name: "slice",
initialState: {
user: undefined,
post: undefined
},
reducers: {},
extraReducers: {
[fetchPost.fulfilled]: (state, action) => {
state.post = action.payload;
},
[fetchUser.fulfilled]: (state, action) => {
state.user = action.payload;
}
}
});
const store = configureStore({ reducer: { slice: slice.reducer } });
const PostHeader = ({ postId }) => {
const dispatch = useDispatch();
const post = useSelector((state) => state.slice.post);
const user = useSelector((state) => state.slice.user);
console.log(post, user);
useEffect(() => {
const loadPost = async () => {
dispatch(fetchPost(postId));
};
loadPost();
}, [postId, dispatch]); // run once on mount
useEffect(() => {
const loadUser = async (userId) => {
dispatch(fetchUser(userId));
};
// check if post has has been set and has a userId
if (post && post.userId) {
loadUser(post.userId);
}
}, [post, dispatch]); // run when post changes
return (
<div>
{!!post && <h1>{post.title}</h1>}
{!!user && <h2>By {user.name}</h2>}
</div>
);
};
export default function App() {
return (
<Provider store={store}>
<PostHeader postId={1} />
</Provider>
);
}