如何使用redux-thunk,redux-saga,suspense和hooks在React中加载数据

琴正初
2023-12-01

by Valerii Tereshchenko

瓦莱里·捷列申科(Valerii Tereshchenko)

如何使用redux-thunk,redux-saga,suspense和hooks在React中加载数据 (How to load data in React with redux-thunk, redux-saga, suspense & hooks)

介绍 (Introduction)

React is a JavaScript library for building user interfaces. Very often using React means using React with Redux. Redux is another JavaScript library for managing global state. Sadly, even with these two libraries there is no one clear way how to handle asynchronous calls to the API (backend) or any other side effects.

React是一个用于构建用户界面JavaScript库。 通常,使用React意味着将React与Redux结合使用Redux是另一个用于管理全局状态JavaScript库。 遗憾的是,即使有这两个库,也没有一种明确的方法来处理对API(后端)的异步调用或任何其他副作用。

In this article I’m trying to compare different approaches to solving this problem. Let’s define the problem first.

在本文中,我试图比较解决此问题的不同方法。 让我们先定义问题。

Component X is one of the many components of the web site (or mobile, or desktop application, it’s also possible). X queries and shows some data loaded from the API. X can be page or just part of the page. Important thing that X is a separate component which should be loosely coupled with the rest of the system (as much as possible). X should show loading indicator while data is retrieving and error if call fails.

组件X是网站的许多组件之一(也可以是移动或桌面应用程序)。 X查询并显示一些从API加载的数据。 X可以是页面,也可以只是页面的一部分。 重要的是X是一个独立的组件,应与系统的其余部分(尽可能多)松散耦合。 X在数据检索时应显示加载指示符,如果调用失败,则应显示错误。

This article assumes that you already have some experience with creating React/Redux applications.

本文假设您已经具有创建React / Redux应用程序的经验。

This article is going to show 4 ways of solving this problem and compare pros and cons of each one. It isn’t a detailed manual on how to use thunk, saga, suspence or hooks.

本文将展示解决此问题的4种方法,并比较每种方法的优缺点它不是有关如何使用thunk,saga,suspence或hooks的详细手册

Code of these examples is available on GitHub.

这些示例的代码可在GitHub上获得

最初设定 (Initial setup)

模拟服务器 (Mock Server)

For testing purposes we are going to use json-server. It’s an amazing project that allows you to build fake REST APIs very fast. For our example, it looks like this.

为了进行测试,我们将使用json-server 。 这是一个了不起的项目,可让您快速构建伪造的REST API。 对于我们的示例,它看起来像这样。

const jsonServer = require('json-server');const server = jsonServer.create();const router = jsonServer.router('db.json');const middleware = jsonServer.defaults();
server.use((req, res, next) => {   setTimeout(() => next(), 2000);});server.use(middleware);server.use(router);server.listen(4000, () => {   console.log(`JSON Server is running...`);});

Our db.json file contains test data in json format.

我们的db.json文件包含json格式的测试数据。

{ "users": [   {     "id": 1,     "firstName": "John",     "lastName": "Doe",     "active": true,     "posts": 10,     "messages": 50   },   ...   {     "id": 8,     "firstName": "Clay",     "lastName": "Chung",     "active": true,     "posts": 8,     "messages": 5   } ]}

After starting the server, a call to the http://localhost:4000/users returns the list of the users with an imitation of delay — about 2s.

启动服务器后,对http:// localhost:4000 / users的调用会返回用户列表,并带有延迟(大约2秒)。

项目和API调用 (Project and API call)

Now we are ready to start coding. I assume that you already have a React project created using create-react-app with Redux configured and ready to use.

现在我们准备开始编码。 我假设您已经有一个使用create-react-app创建的React项目,并且已配置Redux并准备使用它。

If you have any difficulties with it you can check out this and this.

如果您有任何困难,可以查看thisthis

The next step is to create a function to call the API (api.js):

下一步是创建一个函数来调用API( api.js ):

const API_BASE_ADDRESS = 'http://localhost:4000';
export default class Api {   static getUsers() {       const uri = API_BASE_ADDRESS + "/users";
return fetch(uri, {           method: 'GET'       });   }}

Redux-thunk (Redux-thunk)

Redux-thunk is a recommended middleware for basic Redux side effects logic, such as simple async logic (like a request to the API). Redux-thunk itself doesn’t do a lot. It’s just 14!!! lines of the code. It just adds some “syntax sugar” and nothing more.

建议使用Redux-thunk作为基本Redux副作用逻辑的中间件,例如简单的异步逻辑(例如对API的请求)。 Redux-thunk本身并没有做什么。 才14点!!! 所述 代码 。 它只是添加了一些“语法糖”,仅此而已。

The flowchart below helps to understand what we are going to do.

下面的流程图有助于了解我们将要做什么。

Every time an action is performed, the reducer changes state accordingly. The component maps state to properties and uses these properties in the revder() method to figure out what the user should see: a loading indicator, data or error message.

每次执行操作时,减速器都会相应地更改状态。 该组件将状态映射到属性,并在revder()方法中使用这些属性来弄清楚用户应该看到的内容:加载指示符,数据或错误消息。

To make it work we need to do 5 things.

要使其工作,我们需要做5件事。

1.安装垃圾桶 (1. Install tunk)

npm install redux-thunk

2.配置存储时添加thunk中间件(configureStore.js) (2. Add thunk middleware when configuring store (configureStore.js))

import { applyMiddleware, compose, createStore } from 'redux';import thunk from 'redux-thunk';import rootReducer from './appReducers';
export function configureStore(initialState) { const middleware = [thunk];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const store = createStore(rootReducer, initialState, composeEnhancers(applyMiddleware(...middleware)));
return store;}

In lines 12–13 we also configure redux devtools. A bit later it will help to show one of the problems with this solution.

在第12-13行中,我们还配置了redux devtools 。 稍后,它将有助于显示此解决方案的问题之一。

3.创建动作(redux-thunk / actions.js) (3. Create actions (redux-thunk/actions.js))

import Api from "../api"
export const LOAD_USERS_LOADING = 'REDUX_THUNK_LOAD_USERS_LOADING';export const LOAD_USERS_SUCCESS = 'REDUX_THUNK_LOAD_USERS_SUCCESS';export const LOAD_USERS_ERROR = 'REDUX_THUNK_LOAD_USERS_ERROR';
export const loadUsers = () => dispatch => {   dispatch({ type: LOAD_USERS_LOADING });
Api.getUsers()       .then(response => response.json())       .then(           data => dispatch({ type: LOAD_USERS_SUCCESS, data }),           error => dispatch({ type: LOAD_USERS_ERROR, error: error.message || 'Unexpected Error!!!' })       )};

It’s also recommended to have your action creators separated (it adds some additional coding), but for this simple case I think it’s acceptable to create actions “on the fly”.

还建议将动作创建者分开(这会增加一些额外的编码),但是对于这种简单情况,我认为“即时”创建动作是可以接受的。

4.创建reducer(redux-thunk / reducer.js) (4. Create reducer (redux-thunk/reducer.js))

import {LOAD_USERS_ERROR, LOAD_USERS_LOADING, LOAD_USERS_SUCCESS} from "./actions";
const initialState = {   data: [],   loading: false,   error: ''};
export default function reduxThunkReducer(state = initialState, action) {   switch (action.type) {       case LOAD_USERS_LOADING: {           return {               ...state,               loading: true,               error:''           };       }       case LOAD_USERS_SUCCESS: {           return {               ...state,               data: action.data,               loading: false           }       }       case LOAD_USERS_ERROR: {           return {               ...state,               loading: false,               error: action.error           };       }       default: {           return state;       }   }}

5.创建连接到redux的组件(redux-thunk / UsersWithReduxThunk.js) (5. Create component connected to redux (redux-thunk/UsersWithReduxThunk.js))

import * as React from 'react';import { connect } from 'react-redux';import {loadUsers} from "./actions";
class UsersWithReduxThunk extends React.Component {   componentDidMount() {       this.props.loadUsers();   };
render() {       if (this.props.loading) {           return &lt;div>Loading</div>       }
if (this.props.error) {           return &lt;div style={{ color: 'red' }}>ERROR: {this.props.error}</div>       }
return (           &lt;table>               <thead>                   <tr>                       <th>First Name</th&gt;                       <th>Last Name</th&gt;                       &lt;th>;Active?</th>                       <th>Posts</th&gt;                       <th>Messages</th>                   &lt;/tr>               </thead>               <tbody>               {this.props.data.map(u =>                   <tr key={u.id}>                       &lt;td&gt;{u.firstName}</td>                       &lt;td>{u.lastName}</td&gt;                       <td>{u.active ? 'Yes' : 'No'}</td>                       <;td&gt;{u.posts}</td>                       <td>{u.messages}</td>                   </tr>               )}               </tbody>           </table>       );   }}
const mapStateToProps = state => ({   data: state.reduxThunk.data,   loading: state.reduxThunk.loading,   error: state.reduxThunk.error,});
const mapDispatchToProps = {   loadUsers};
export default connect(   mapStateToProps,   mapDispatchToProps)(UsersWithReduxThunk);

I tried to make the component as simple as possible. I understand that it looks awful :)

我试图使组件尽可能简单。 我了解它看起来很糟糕:)

Loading indicator

加载指示器

Data

数据

Error

错误

There you have it: 3 files, 109 line of code (13(actions) + 36(reducer) + 60(component)).

那里有:3个文件,109行代码(13(动作)+ 36(归约器)+ 60(组件))。

优点: (Pros:)

  • “Recommended” approach for react/redux applications.

    适用于react / redux应用程序的“推荐”方法。
  • No additional dependencies. Almost, thunk is tiny :)

    没有其他依赖项。 差不多,thunk很小:)
  • No need to learn new things.

    无需学习新事物。

缺点: (Cons:)

  • A lot of code in different places

    不同地方有很多代码
  • After navigation to another page, old data is still in the global state (see picture below). This data is outdated and useless information that consumes memory.

    导航到另一页后,旧数据仍处于全局状态(请参见下图)。 此数据已过时,并且浪费了内存的无用信息。
  • In case of complex scenarios (multiple conditional calls in one action, etc.) code isn’t very readable

    在复杂的情况下(一个动作中有多个条件调用等),代码不太可读

Redux传奇 (Redux-saga)

Redux-saga is a redux middleware library designed to make handling side effects easy and readable. It leverages ES6 Generators which allows us to write asynchronous code that looks synchronous. Also, this solution is easy to test.

Redux-saga是一个Redux中间件库,旨在使处理副作用变得容易且可读。 它利用ES6生成器,使我们能够编写看起来是同步的异步代码。 而且,此解决方案易于测试。

From a high level perspective, this solution works the same as thunk. The flowchart from the thunk example is still applicable.

从高级的角度来看,此解决方案与thunk相同。 thunk示例中的流程图仍然适用。

To make it work we need to do 6 things.

要使其工作,我们需要做6件事。

1.安装传奇 (1. Install saga)

npm install redux-saga

2.添加传奇中间件并添加所有sagas(configureStore.js) (2. Add saga middleware and add all sagas (configureStore.js))

import { applyMiddleware, compose, createStore } from 'redux';import createSagaMiddleware from 'redux-saga';import rootReducer from './appReducers';import usersSaga from "../redux-saga/sagas";
const sagaMiddleware = createSagaMiddleware();
export function configureStore(initialState) { const middleware = [sagaMiddleware];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const store = createStore(rootReducer, initialState, composeEnhancers(applyMiddleware(...middleware)));
sagaMiddleware.run(usersSaga);
return store;}

Sagas from line 4 will be added in step 4.

来自第4行的Sagas将在步骤4中添加。

3.创建动作(redux-saga / actions.js) (3. Create action (redux-saga/actions.js))

export const LOAD_USERS_LOADING = 'REDUX_SAGA_LOAD_USERS_LOADING';export const LOAD_USERS_SUCCESS = 'REDUX_SAGA_LOAD_USERS_SUCCESS';export const LOAD_USERS_ERROR = 'REDUX_SAGA_LOAD_USERS_ERROR';
export const loadUsers = () => dispatch => {   dispatch({ type: LOAD_USERS_LOADING });};

4.创建sagas(redux-saga / sagas.js) (4. Create sagas (redux-saga/sagas.js))

import { put, takeEvery, takeLatest } from 'redux-saga/effects'import {loadUsersSuccess, LOAD_USERS_ERROR, LOAD_USERS_LOADING, LOAD_USERS_SUCCESS} from "./actions";import Api from '../api'
async function fetchAsync(func) {   const response = await func();
if (response.ok) {       return await response.json();   }
throw new Error("Unexpected error!!!");}
function* fetchUser() {   try {       const users = yield fetchAsync(Api.getUsers);
yield put({type: LOAD_USERS_SUCCESS, data: users});   } catch (e) {       yield put({type: LOAD_USERS_ERROR, error: e.message});   }}
export function* usersSaga() {   // Allows concurrent fetches of users   yield takeEvery(LOAD_USERS_LOADING, fetchUser);
// Does not allow concurrent fetches of users   // yield takeLatest(LOAD_USERS_LOADING, fetchUser);}
export default usersSaga;

Saga has quite a steep learning curve, so if you’ve never used it and have never read anything about this framework it could be difficult to understand what’s going on here. Briefly, in the userSaga function we configure saga to listen to the LOAD_USERS_LOADING action and trigger the fetchUsers function. The fetchUsers function calls the API. If the call succeeds, then the LOAD_USER_SUCCESS action is dispatched, otherwise the LOAD_USER_ERROR action is dispatched.

Saga的学习曲线相当陡峭,因此,如果您从未使用过它,并且从未阅读过有关该框架的任何内容,那么可能很难理解这里发生的事情。 简要地说,在userSaga函数中,我们将saga配置为侦听LOAD_USERS_LOADING操作并触发fetchUsers函数。 fetchUsers函数调用API。 如果调用成功,则调度LOAD_USER_SUCCESS操作,否则调度LOAD_USER_ERROR操作。

5.创建reducer(redux-saga / reducer.js) (5. Create reducer (redux-saga/reducer.js))

import {LOAD_USERS_ERROR, LOAD_USERS_LOADING, LOAD_USERS_SUCCESS} from "./actions";
const initialState = {   data: [],   loading: false,   error: ''};
export default function reduxSagaReducer(state = initialState, action) {   switch (action.type) {       case LOAD_USERS_LOADING: {           return {               ...state,               loading: true,               error:''           };       }       case LOAD_USERS_SUCCESS: {           return {               ...state,               data: action.data,               loading: false           }       }       case LOAD_USERS_ERROR: {           return {               ...state,               loading: false,               error: action.error           };       }       default: {           return state;       }   }}

The reducer here is absolutely the same as in the thunk example.

这里的reducer与thunk示例完全相同。

6.创建连接到redux的组件(redux-saga / UsersWithReduxSaga.js) (6. Create component connected to redux (redux-saga/UsersWithReduxSaga.js))

import * as React from 'react';import {connect} from 'react-redux';import {loadUsers} from "./actions";
class UsersWithReduxSaga extends React.Component {   componentDidMount() {       this.props.loadUsers();   };
render() {       if (this.props.loading) {           return &lt;div>Loading</div>       }
if (this.props.error) {           return &lt;div style={{color: 'red'}}>ERROR: {this.props.error}</div>       }
return (           &lt;table>               <thead>                   <tr>                       <th>First Name</th&gt;                       <th>Last Name</th&gt;                       &lt;th>;Active?</th>                       <th>Posts</th&gt;                       <th>Messages</th>                   </tr>;               </thead>               <tbody>                   {this.props.data.map(u =>                       <tr key={u.id}>                           &lt;td&gt;{u.firstName}&lt;/td>                           <td>;{u.lastName}</td>                           <td>{u.active ? 'Yes' : 'No'}</td>                           <td>{u.posts}&lt;/td>                           <td>{u.messages}</td>                       </tr>                   )}               </tbody>           </table>       );   }}
const mapStateToProps = state => ({   data: state.reduxSaga.data,   loading: state.reduxSaga.loading,   error: state.reduxSaga.error,});
const mapDispatchToProps = {   loadUsers};
export default connect(   mapStateToProps,   mapDispatchToProps)(UsersWithReduxSaga);

The component is also almost the same here as in the thunk example.

这里的组件也与thunk示例中的组件几乎相同。

So here we have 4 files, 136 line of code (7(actions) + 36(reducer) + sagas(33) + 60(component)).

因此,这里有4个文件,136行代码(7(动作)+ 36(减速器)+ sagas(33)+ 60(组件))。

优点: (Pros:)

  • More readable code (async/await)

    更具可读性的代码(异步/等待)
  • Good for handling complex scenarios (multiple conditional calls in one action, action can have multiple listeners, canceling actions, etc.)

    适用于处理复杂的场景(一个动作中有多个条件调用,一个动作可以有多个侦听器,取消多个动作等)
  • Easy to unit test

    易于单元测试

缺点: (Cons:)

  • A lot of code in different places

    不同地方有很多代码
  • After navigation to another page, old data is still in the global state. This data is outdated and useless information that consumes memory.

    导航到另一个页面后,旧数据仍处于全局状态。 此数据已过时,并且浪费了内存的无用信息。
  • Additional dependency

    附加依赖
  • A lot of concepts to learn

    很多概念需要学习

悬念 (Suspense)

Suspense is a new feature in React 16.6.0. It allows us to defer rendering part of the component until some condition is met (for example data from the API loaded).

Suspense是React 16.6.0中的新功能。 它使我们可以推迟渲染组件的一部分,直到满足某些条件(例如,从API加载的数据)为止。

To make it work we need to do 4 things (it’s definitely getting better :) ).

为了使其正常工作,我们需要做4件事(它肯定会变得越来越好:))。

1.创建缓存(suspense / cache.js) (1. Create cache (suspense/cache.js))

For the cache, we are going to use a simple-cache-provider which is a basic cache provider for react applications.

对于缓存,我们将使用简单缓存提供程序 ,它是React应用程序的基本缓存提供程序。

import {createCache} from 'simple-cache-provider';
export let cache;
function initCache() { cache = createCache(initCache);}
initCache();

2.创建错误边界(suspense / ErrorBoundary.js) (2. Create Error Boundary (suspense/ErrorBoundary.js))

This is an Error Boundary to catch errors thrown by Suspense.

这是一个错误边界,用于捕获Suspense引发的错误。

import React from 'react';
export class ErrorBoundary extends React.Component { state = {};
componentDidCatch(error) {   this.setState({ error: error.message || "Unexpected error" }); }
render() {   if (this.state.error) {     return &lt;div style={{ color: 'red' }}>ERROR: {this.state.error || 'Unexpected Error'}</div>;   }
return this.props.children; }}
export default ErrorBoundary;

3.创建用户表(suspense / UsersTable.js) (3. Create Users Table (suspense/UsersTable.js))

For this example, we need to create an additional component which loads and shows data. Here we are creating a resource to get data from the API.

对于此示例,我们需要创建一个额外的组件来加载和显示数据。 在这里,我们正在创建资源以从API获取数据。

import * as React from 'react';import {createResource} from "simple-cache-provider";import {cache} from "./cache";import Api from "../api";
let UsersResource = createResource(async () => {   const response = await Api.getUsers();   const json = await response.json();
return json;});
class UsersTable extends React.Component {   render() {       let users = UsersResource.read(cache);
return (           &lt;table>               <thead>               <tr>                   <th>First Name&lt;/th>                   <th>;Last Name&lt;/th>                   <th&gt;Active?</th>                   <th>Posts&lt;/th>                   <th>Messages</th>               </tr>               </thead&gt;               <tbody&gt;               {users.map(u =>                   <tr key={u.id}>                       &lt;td&gt;{u.firstName}</td>                       &lt;td>{u.lastName}</td&gt;                       <td>{u.active ? 'Yes' : 'No'}</td>                       <;td&gt;{u.posts}</td>                       <td>{u.messages}</td>                   </tr>               )}               </tbody>           </table>       );   }}
export default UsersTable;

4.创建组件(suspense / UsersWithSuspense.js) (4. Create component (suspense/UsersWithSuspense.js))

import * as React from 'react';import UsersTable from "./UsersTable";import ErrorBoundary from "./ErrorBoundary";
class UsersWithSuspense extends React.Component {   render() {       return (           &lt;ErrorBoundary>               <React.Suspense fallback={<div&gt;Loading</div>}&gt;                   <UsersTable/>               &lt;/React.Suspense>           </ErrorBoundary>       );   }}
export default UsersWithSuspense;

4 files, 106 line of code (9(cache) + 19(ErrorBoundary) + UsersTable(33) + 45(component)).

4个文件,106行代码(9(缓存)+ 19(ErrorBoundary)+ UsersTable(33)+ 45(component))。

3 files, 87 line of code (9(cache) + UsersTable(33) + 45(component)) if we assume that ErrorBoundary is a reusable component.

如果我们假定ErrorBoundary是可重用的组件,则3个文件,87行代码(9(缓存)+ UsersTable(33)+ 45(组件))。

优点: (Pros:)

  • No redux needed. This approach can be used without redux. Component is fully independent.

    不需要redux。 此方法可以不使用redux来使用。 组件是完全独立的。
  • No additional dependencies (simple-cache-provider is part of React)

    没有额外的依赖关系( simple-cache-provider是React的一部分)

  • Delay of showing Loading indicator by setting dellayMs property

    通过设置dellayMs属性延迟显示“加载”指示器
  • Fewer lines of code than in previous examples

    比前面的示例更少的代码行

缺点: (Cons:)

  • Cache is needed even when we don’t really need caching.

    即使我们不需要缓存,也需要缓存。
  • Some new concepts need to be learned (which are part of React).

    需要学习一些新概念(这是React的一部分)。

钩子 (Hooks)

At the time of writing this article, hooks have not officially been released yet and available only in the “next” version. Hooks are indisputably one of the most revolutionary upcoming features which can change a lot in the React world very soon. More details about hooks can be found here and here.

在撰写本文时,挂钩尚未正式发布,仅在“下一个”版本中可用。 毫无疑问,挂钩是即将来临的最具革命性的功能之一,它很快就会在React世界中发生很大的变化。 有关钩子的更多详细信息,请参见此处此处

To make it work for our example we need to do one(!) thing:

为了使其适用于我们的示例,我们需要做一个(!)事情:

1.创建和使用钩子(hooks / UsersWithHooks.js) (1. Create and use hooks (hooks/UsersWithHooks.js))

Here we are creating 3 hooks (functions) to “hook into” React state.

在这里,我们将创建3个挂钩(函数)以“挂钩”到React状态。

import React, {useState, useEffect} from 'react';import Api from "../api";
function UsersWithHooks() {   const [data, setData] = useState([]);   const [loading, setLoading] = useState(true);   const [error, setError] = useState('');
useEffect(async () => {       try {           const response = await Api.getUsers();           const json = await response.json();
setData(json);       } catch (e) {           setError(e.message || 'Unexpected error');       }
setLoading(false);   }, []);
if (loading) {       return &lt;div>Loading</div>   }
if (error) {       return &lt;div style={{color: 'red'}}>ERROR: {error}</div>   }
return (       &lt;table>           <thead>           <tr>;               <th>First Name</th>               <th&gt;Last Name</th>               &lt;th>Active?</th>               <th&gt;Posts</th&gt;               &lt;th>Messages</th>           </tr&gt;           </thead>           <tbody>           {data.map(u =&gt;               <tr key={u.id}>;                   &lt;td>;{u.firstName}</td>;                   <td>{u.lastName}&lt;/td&gt;                   &lt;td>{u.active ? 'Yes' : 'No'}<;/td>                   <td>{u.posts}</td>                   <td>{u.messages}</td>               </tr>           )}           </tbody>       </table>   );}
export default UsersWithHooks;

And that’s it — just 1 file, 56 line of code!!!

就是这样-仅1个文件,共56行代码!!!

优点: (Pros:)

  • No redux needed. This approach can be used without redux. Component is fully independent.

    不需要redux。 此方法可以不使用redux来使用。 组件是完全独立的。
  • No additional dependencies

    没有其他依赖
  • About 2 times less code than in other solutions

    大约比其他解决方案少2倍的代码

缺点: (Cons:)

  • At first look, the code looks weird and difficult to read and understand. It will take some time to get used to hooks.

    乍一看,该代码看起来很奇怪,难以阅读和理解。 习惯钩子需要一些时间。
  • Some new concepts need to be learned (which are part of React)

    需要学习一些新概念(这是React的一部分)
  • Not officially released yet

    尚未正式发布

结论 (Conclusion)

Let’s organize these metrics as a table first.

首先让我们将这些指标组织为表格。

  • Redux is still a good option to manage global state (if you have it)

    Redux仍然是管理全局状态的好选择(如果有的话)
  • Each option has pros and cons. Which approach is better depends on the project: its complexity, use cases, team knowledge, when the project is going to production, etc.

    每个选项都有优点和缺点。 哪种方法更好取决于项目:项目的复杂性,用例,团队知识,项目何时投产等。
  • Saga can help with complex use cases

    Saga可以帮助解决复杂的用例
  • Suspense and Hooks are both worth considering (or at least learning) especially for new projects

    悬念和挂钩都值得考虑(或至少要学习),特别是对于新项目

That’s it — enjoy and happy coding!

就是这样-享受愉快的编码!

翻译自: https://www.freecodecamp.org/news/loading-data-in-react-redux-thunk-redux-saga-suspense-hooks-666b21da1569/

 类似资料: