如何在另一个 asyncThunk 中调度 asyncThunk

Lor*_*Dex 4 reactjs redux redux-toolkit

我创建了两个异步重击:

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
    }
);
Run Code Online (Sandbox Code Playgroud)

基本上我试图在 React 组件的 useEffectfoo()内部调用bar() ,我bar()像这样调度:

React.useEffect( async () => {
    await dispatch( bar() );
}, [bar]);
Run Code Online (Sandbox Code Playgroud)

foo正在派遣但bar没有。

我究竟做错了什么?

先感谢您。

编辑1。它有点像反模式,但我基本上想做的是链接 bar() 和 foo(),而且我不知道任何其他(正确的?)方式

编辑2。基于@Nadia Chibrikova 评论,这是我使用 thunkAPI 尝试过的:

    export const bar = createAsyncThunk(
        'slice/bar',
        async ( thunkApi ) => {
            thunkApi .dispatch(foo())
        }
    );
Run Code Online (Sandbox Code Playgroud)

但我收到以下错误:

Cannot read property 'dispatch' of undefined
Run Code Online (Sandbox Code Playgroud)

Lin*_*ste 12

链接重击

这不是一个很好的设计模式,但它是可以做到的。您的“Edit2”很接近,但thunkApi它是有效负载创建者的第二个参数。第一个参数是您在调用动作创建者时传递的参数bar(args)。如果不需要参数,则可以使用 a_来表示该变量未使用。

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;
  }
);
Run Code Online (Sandbox Code Playgroud)

调用dispatch(bar())将按以下顺序调度操作:

  1. “切片/条/待定”
  2. “切片/foo/待定”
  3. “切片/foo/已完成”
  4. “切片/条形/完成”

单独的行动

正如 @Marcus Melodious 所建议的,最好有完全独立的操作并在组件中处理分派。如果需要来自(例如 an或 a )bar的某些数据,那么它应该将该数据作为其操作创建者的参数。fooidtoken

这是一个示例,我们获取帖子,然后根据userId该帖子的属性获取用户个人资料。

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();
  }
);
Run Code Online (Sandbox Code Playgroud)

您可以使用一个useEffect并查看调度结果

useEffect(() => {
  const execute = async () => {
    const postAction = await dispatch(fetchPost(POST_ID));
    const {userId} = postAction.payload;
    dispatch(fetchUser(userId));
  };

  execute();
}, []);
Run Code Online (Sandbox Code Playgroud)

或者您可以从两个单独的挂钩发起两个请求useEffect。您的第二个钩子知道何时根据 Redux 状态的变化进行调度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/>;
};
Run Code Online (Sandbox Code Playgroud)

这是两个钩子方法的完整代码示例:

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>
  );
}
Run Code Online (Sandbox Code Playgroud)