Redux-Saga 实例教程:构建 Reddit 的 SPA 应用

Redux-Saga 是 Redux 的一个中间件,它用于处理异步操作和副作用。它类似于 Redux-Thunk,但提供了更多的功能和控制,例如取消异步操作,处理复杂的任务和操作序列等。在本文中,我们将使用 Redux-Saga 构建一个单页应用程序,该应用程序使用 Reddit API 获取新闻和帖子列表,并允许用户通过搜索功能查找这些信息。

什么是 Reddit?

Reddit 是一个全球知名的社交新闻聚合网站,用户可以发布和讨论各种内容,如新闻、图片、视频、游戏、科技、体育等等。Reddit 的用户可以在所述社区内发布内容,通常是以纯文本形式发布,其他用户可在该帖子下面进行讨论和回复。 Reddit 还提供了“投票”功能,可以在投票中选出流行的帖子,使其更加突出。

准备工作

在开始构建RedditSPA之前,请确保您已经安装了必要的软件:

  • Node.js 和 npm 包管理器
  • Yarn 包管理器

本教程还使用了以下工具和库:

  • React 前端框架
  • Redux 状态管理库
  • Redux-Saga 异步中间件
  • Axios HTTP 客户端库
  • React-Router 前端路由库

您可以使用以下命令安装它们:

--- ------- -- ---------------- ----
---------------- ----------
-- ----------
---- --- ----------- ----- ---------- ----- ----------------

我们还需要 Reddit 的开发者 API 密钥。您可以在Reddit 开发者门户注册一个应用程序并获取它。从 Reddit 获取的 API 密钥是机密的,请勿在任何公共代码存储库中包含它。

程序代码已放在 GitHub 上,你可以 clone 到本地执行查看。

实现 Reddit API 接口

为了从 Reddit 的 API 获取信息,我们需要编写一个 API 客户端。使用 Axios 可以轻松地完成 HTTP 请求,而且我们还可以设置请求头和拦截器以处理响应和错误。

------ ----- ---- --------

----- ------- - -------------------------

----- --------------- - --------------
  -------- - ------------- ------------ -
---

------ ------- ----------------

------ ----- ---------- - ----------- ------- --
  ----------------------------------------------------------------

我们主要是使用 axios.create() 方法创建API客户端,指定了 Reddit 的基本 URL,{'User-Agent': 'redux-saga'}} 用于 Reddit API 要求一个请求头部分指定用户代理,我们还定义了一个 getPosts 函数,它使用指定的子Reddit,并向 Reddit API 发出 GET 请求以检索新帖子列表。

构建 Redux Store

在我们的应用程序中,我们需要存储 Reddit API 返回的新闻和文章列表。我们将使用 Redux 来管理应用程序状态,创建一个 Redux store。

Actions

我们使用 Redux 定义一些 actions 来控制 store。我们使用它来请求 Reddit API 并将结果存储在 store 中。我们同时定义了一个 setFilter action 以设置当前 Reddit 主题的过滤器值。

------ ----- ------------- - ----------------
------ ----- ------------- - ----------------
------ ----- ---------------- - -------------------
------ ----- -------------------- - -----------------------
------ ----- ---------- - -------------

------ ----- --------------- - --------- -- --
  ----- -----------------
  ---------
---

------ ----- ------------------- - --------- -- --
  ----- ---------------------
  ---------
---

------ ----- ------------ - --------- -- --
  ----- --------------
  ---------
---

------ ----- ------------ - ----------- ------- ----- -- --
  ----- --------------
  ----------
  -------
  ------ ---------------------------- -- ------------
  ----------- ----------
---

------ ----- --------- - ------ -- --
  ----- -----------
  ------
---

我们使用 type 字段为每个 action 定义一个唯一的名字,payload 字段存储有关请求和响应的数据。在上面的代码中,我们定义了 5 个 actions:

  • SELECT_SUBREDDIT:设置当前 Reddit 的主题子版块。
  • INVALIDATE_SUBREDDIT:通知 Redux store 文章已过期,需要重新加载。
  • REQUEST_POSTS:请求 Reddit API 并通知 store。
  • RECEIVE_POSTS:接收 Reddit API 返回的文章并将其存储在 store 中。
  • SET_FILTER:设置当前 Reddit 主题的过滤器值。

我们需要使用 reducer 函数组合上面的actions

Reducers

Redux 应用的 reducer 管理着它的 state(状态)的更新。我们需要定义以下 actions 的状态存储方式以及如何响应 those actions:

------ -
  --------------
  --------------
  -----------------
  ---------------------
  ----------
- ---- -------------

----- ----- - -
  ----- - -
    ----------- ------
    -------------- ------
    ------ --
  --
  ------
- -- -
  ------ ------------- -
    ---- ---------------------
      ------ -
        ---------
        -------------- ----
      --
    ---- --------------
      ------ -
        ---------
        ----------- -----
        -------------- -----
      --
    ---- --------------
      ------ -
        ---------
        ----------- ------
        -------------- ------
        ------ -------------
        ------------ ------------------
        ------- -------------
      --
    --------
      ------ ------
  -
--

----- --------- - -
  ----- - ---
  ------
- -- -
  ------ ------------- -
    ---- -----------------
      ------ -----------------
    --------
      ------ ------
  -
--

----- ------ - -
  ----- - ------
  ------
- -- -
  ------ ------------- -
    ---- -----------
      ------ --------------
    --------
      ------ ------
  -
--

----- ---------------- - -
  ----- - - --
  ------
- -- -
  ------ ------------- -
    ---- ---------------------
    ---- --------------
    ---- --------------
      ------ -
        ---------
        ------------------- ------------------------------ -------
      --
    --------
      ------ ------
  -
--

------ ------- -----------------

------ ----- --------- - ----- -- -------------
------ ----- ------------ - ----- -- ----------------
------ ----- -------- - ----- -- ------------
------ ----- -------------- - ----- -- ------------------
------ ----- ------------- - ----- -- -----------------
------ ----- ---------------- - ----- -- --------------------
------ ----- ----------------- - ------- -- -
  ----- --------- - --------------------
  ------ ---------------- -- ---
--

Redux Store 由四个 reducer 函数组成:

  • subreddit:纯函数,存储当前 Reddit 的子版块名称。
  • filter:纯函数,存储当前 Reddit 主题的过滤器值。
  • posts:纯函数,存储 Reddit API 返回的新闻和文章列表。
  • postsBySubreddit:纯函数,用来组合上面的 three reducer,并以“子Reddit”为键存储他们的值。

注意以上的reducer函数操作每个状态域的方法,分别是状态key项的名称(如上的 items),不同action可能会改变相同或不同的键的值。

Store

Store 定义了 actions、reducers 和中间件的集合。在我们的应用程序中,它通过存储所有 Reddit 相关的状态信息来帮助我们控制整个应用程序的行为。

------ - ------------ --------------- - ---- --------
------ -------------------- ---- -------------
------ ----------- ---- --------------
------ -------- ---- -----------

----- -------------- - ---------------- -- -
  ----- -------------- - -----------------------
  ----- ----- - ------------
    ------------
    ---------------
    -------------------------------
  --
  -----------------------------
  ------ ------
--

------ ------- ---------------

其中 rootSaga 是 Redux Saga 应用程序里的核心逻辑部分。

实现 Reddit Saga

我们使用 Redux-Saga 中间件模块来处理异步操作和副作用。Sagas 是一些用于处理副作用(如读取和写入磁盘、发出 HTTP 请求、访问浏览器缓存等)的函数, Saga 将它们的执行与 Redux Store 的状态同步。在本文中,我们将大量使用 Sagas 来管理应用程序中的所有操作。

Reddit Api Saga,处理请求和响应

以下是 Reddit API 请求和响应过程的 Saga 函数。

requests-saga.js

------ - ----- ---- ---------- - ---- ---------------------
------ - ------------- --------- - ---- --------------
------ - ----------- ------------- ------------- - ---- -------------

------ --------- ---------------- -
  --- -
    ----- --------- - ----- ---------------
    ----- ------ - ----- ------------
    ----- -------- - ----- ---------------- ---------- --------
    ----- --------------------------- ------- ----------------
  - ----- --- -
    ---------------
  -
-

------ ------- --------- ----------------- -
  ----- ------------------------- ----------------
-

这里我们引入了一些 Redux-Saga 的顶级函数:

  • takeLatest:对于同一个 action 的每个新实例,以非阻止屏幕模式较新的 call fork cancel 效果来选择仅最新的一个发生,并立即取代之前的 saga。
  • call:在 Saga 中调用函数。
  • put:分发一个 Action。
  • getSubreddit / getFilter:从 store 中选择当前 Reddit 主题和过滤器值,以便向 Reddit API 发出正确的请求。
  • fetchPosts:由我们之前实现的我们的Reddit客户端调用Reddit API。

类 Reddit 页面 Saga

在 Reddit API 请求成功后,我们需要将 HTML 格式的结果渲染为页面元素。我们使用 React 组件来处理 UI,并将 Sagas 用于一些类似于组件生命周期函数的操作。下面是约会 Reddit 页面的 Saga 函数。

front-page-saga.js

------ - ----- ---- ------- ---------- - ---- ---------------------
------ -
  --------------
  -----------------
  -----------
- ---- -------------
------ - -------------- - ---- ------------------
------ -
  ---------
  ------------------
  -----------------
  --------------
  ---------------
  ---------
- ---- --------------

------ --------- ------------------------- -
  ----- --------- - ----- ---------------------
  ----- ---------------------
-

------ --------- --------------------- -
  ----- ---------------------
-

------ --------- --------------- -
  ----- ---------------------
-

------ --------- ------------------ -
  ----- ---------- - ----- ----------------------
  ----- ------------- - ----- -------------------------
  ----- ----------- - ----- -----------------------
  ------ ----------- -- -------------- -- --------------
-

------ --------- ------------------------ -
  ----- ------------- - ----- --------------------------
  ----- ----------- - ----- -------------------
  -- ------------- -
    ----- ----- ----- --------------- ---
  -
-

------ ------- --------- -------------- -
  ----- ---------------------------- ---------------------
  ----- ---------------------- ---------------
  ----- ------------------------- ------------------------
-

我们定义了 5 个 Saga 函数:

  • invalidateSubredditSaga:通知 store subreddit 已失效,需要重新请求新的文章。
  • selectSubredditSaga:使用 Reddit API 请求和响应新的文章、更新 store 中的列表。
  • setFilterSaga:使用 Reddit API 请求和响应过滤器,更新 store 中的 filter 状态。
  • shouldFetchPosts:检查 store 是否具有失效状态、未开始拉取状态、已过期状态。 如果是,我们需要重新请求新的文章。
  • fetchPostsIfNeededSaga:该 Saga 函数根据 shouldFetchPosts() 执行是否需要更新 Reddit API,如果需要,则发送 get 请求来获取 Reddit API 新的文章。

以上 5 个 Saga 函数用于监听 Reddit 页面动作,并在页面发生上述动作时调用。

Root Saga:组合所有 Sagas

我们使用根 Saga 将上述所有 Sagas 组合在一起。

------ - --- - ---- ---------------------
------ --------------- ---- ------------------
------ ------------ ---- --------------------

------ ------- --------- ---------- -
  ----- -----
    ------------------
    --------------
  ---
-

我们不需要使用一个 Saga 来处理所有的 action,而是使用多个 Saga 并使用 all() 函数将它们组合在一起。

渲染 Reddit SPA 应用

我们需要定义一个使用 React 的 Reddit App 组件,以便渲染 Reddit 应用。 Reddit App 具有以下功能:

  • Header:应用程序的标题以及 Reddit 的子版块的菜单。
  • Sidebar:显示 Reddit 主题的过滤器。
  • Main:Reddit 的新闻和文章列表。
  • 新闻和文章列表项。

在 Redux 中,我们将 Reddit App 整体作为一个容器组件实现,Redux 将负责管理 Reddit 的状态信息。根据 store 中的数据,AppComponent会从 Reddit.API 获取数据,并将其传递给 Reddit.Content.

App.jsx

------ ------ - --------- - ---- --------
------ --------- ---- -------------
------ - ------- - ---- --------------
------ - ---------------- -------------------- ---------- ------------ - ---- ------------
------ ------ ---- ----------------------
------ ------ ---- ----------------------
------ ------- ---- -----------------------
------ ------- ---- -----------------------
------ -
  -------------
  ----------
  ---------
  --------------
  ----------------
- ---- -------------

----- --- - --
  ---------- ------- ------ ----------- -------------- --------
-- -- -
  ------------ -- -
    -------------------------
  -- ----------- ---------

  ------ -
    --
      ------- --
      ---- ----------------------
        --------
          ---------------------
          -------------
          -----------------------
          -----------------------------
          ---------------------------- -- -------------------------------------
          ------------------------- -- --------------------------------
        --
        -------
          ---------------
          ---------------- -- ----------------------------
        --
        --------
          -------------
          -----------------------
          -----------------------------
        --
      ------
    ---
  --
--

------------- - -
  ---------- ----------------------------
  ------- ----------------------------
  ------ ---------------------------
  ----------- --------------------------
  -------------- --------------------------
  --------- --------------------------
--

----- --------------- - ----- -- --
  ---------- --------------------
  ------- -----------------
  ------ ----------------
  ----------- ---------------------
  -------------- ------------------------
---

------ ------- ------------------------------

组件

下面是 Reddit 应用程序的组件定义。

Header.jsx

------ ----- ---- --------

----- ------ - -- -- -
  -----
    ---- ------------------------
      -- -------- --------------------- -------------- -------
    ------
  ------
--

------ ------- -------

Sidebar.jsx

------ ----- ---- --------
------ --------- ---- -------------
------ ---------- ---- -------------
------ - --------------- - ---- ---------------------------------
------ - --------- - ---- ------------------------------------
------ - ------------------ - ---- ------------------------------------
------ --------- ---- --------------

----- ------- - --
  ---------- ------ ----------- -------------- ------------------ ---------------------
-- -- -
  ---- ----------------
    ---- -------------- --- ----
      ---- -----------------
        ---- -------------------------
          ---------- - --------------
          --- -----------------------
            -------------- -- -
              ----------
                ---------
                ---------------
                ------------------- --- ----
                ----------- -- -----------------------
              --
            ---
          -----
        ------
        ---- ------------------------
          ----------- -- -------------- -- -
            ------
              -----------
              ---------------- ---------------- ---- --
            -------
          --
          ------------ -- ------------- -- -
            ------- ------------- ----------- -- -------------------------
              -----
            ---------
          --
          ------------ -- -------------- -- -
            ------- ------------- ----------- -- -------------------------
              -------
              ---------------- ------------------------- --
            ---------
          --
        ------
      ------
    ------
  ------
--

----------------- - -
  ---------- ----------------------------
  ------ ---------------------------
  ----------- --------------------------
  -------------- --------------------------
  ------------------ --------------------------
  ---------------------- --------------------------
--

------ ------- --------

Subreddit.jsx

------ ----- ---- --------
------ --------- ---- -------------

----- --------- - -- ---------- --------- ------- -- -- -
  ---
    ------------------- - ---------------- ------- - ------------------
    -----------------
  -
    -----------
  -----
--

------------------- - -
  ---------- ----------------------------
  --------- --------------------------
  -------- --------------------------
--

------ ------- ----------

Filter.jsx

------ ----- ---- --------
------ --------- ---- -------------

----- ------ - -- ------- -------- -- -- -
  -----
    --- ------------------------------- ------------
    ---- ------------------------
      ------- --------------------------- -------------- ----------- -- --------------------------
        ------- ------------------------
        ------- ------------------------
        ------- ------------------------------
      ---------
    ------
  ------
--

---------------- - -
  ------- ----------------------------
  --------- --------------------------
--

------ ------- -------

Content.jsx

------ ----- ---- --------
------ --------- ---- -------------
------ ---------- ---- -------------
------ - --------------- - ---- ---------------------------------
------ - --------- - ---- ------------------------------------
------ - --------------------- - ---- ------------------------------------
------ ------- ---- ------------

----- ------- - -- ------ ----------- ------------- -- -- -
  ---- -------------- --- ----
    -------------- -- ----------- -- -
      ---- -------------------------
        ------ -- ----- ------
        -- --
        ---------------- ---------------------------- --
      ------
    --
    ----------- -- -
      ---- -------------------------
        ---------------- ---------------- ---- --
      ------
    --
    --------------- -- -
      --------
        -------------
        -----------
      --
    ---
  ------
--

----------------- - -
  ------ ---------------------------
  ----------- --------------------------
  -------------- --------------------------
--

------ ------- --------

Article.jsx

------ ----- ---- --------
------ --------- ---- -------------

----- ------- - -- ---- -- -- -
  ---- -----------------
    ---- -------------------------
      ---- -------------- ----------------
        ---- -------------- ----
          ---- -------------------- ---------------- -------------------------- -------- --------- ------ -- --
        ------
        ---- -------------- ----
          ----- ------------------------------------------
          ----------------------
        ------
      ------
    ------
    ---- ------------------------
      -- --------------- --------------- ------------- -------------- -- -----------
    ------
  ------
--

----------------- - -
  ----- ----------------------------
--

------ ------- --------

到此为止,这个 Reddit 应用程序就完成了。

总结

在示例代码中,我使用了 React、Redux 和 Redux-Saga,还使用了 React-Router 和 Axios 库。这些技术和库对于构建单页应用程序非常有效,我们可以模块化地开发应用程序,拆分组件、代码和功能,使代码易于阅读、维护和扩展。

Redux-Saga 可以协调所有异步操作和副作用,使应用程序非常健壮。 Sagas 的执行也更容易进行测试,因为我们可以一步一步地处理所有副作用。此外,Redux-Saga 对于取消异步操作和处理复杂异步操作非常方便,我们只需设置相应的逻辑即可。

重点在 Saga 代码的接收和处理,比较繁琐,要点需要记清楚。将action提交到store,由reducer进行交互,需要注意的是每个reducer的状态域,注意处理方法。

完整的源码可以参见这里

希望对初学Redux和Sagas的读者有所启发和帮助。

来源:JavaScript中文网 ,转载请联系管理员! 本文地址:https://www.javascriptcn.com/post/654f36e97d4982a6eb831d23


猜你喜欢

  • GraphQL:用 Connection 优化节点查询

    前言 GraphQL 是一种由 Facebook 开发的数据查询和操作语言,它提供了一种更高效、更灵活的方式来获取和操作数据。GraphQL 的一个重要特性就是可以精确地指定需要查询的数据,避免了传统...

    2 个月前
  • Server-sent Events 的浏览器支持情况及解决方法

    什么是 Server-sent Events? Server-sent Events(简称 SSE)是一种基于 HTTP 的服务器推送技术,它可以让服务器向客户端发送事件流,客户端通过监听这个事件流来...

    2 个月前
  • ECMAScript 2020(ES11)中的新特性:BigInt 转换

    在 ECMAScript 2020(ES11)中,新增了一种数据类型:BigInt。它是一种可以表示任意大整数的数据类型,可以用来解决 JavaScript 中整数运算的精度问题。

    2 个月前
  • CSS Reset 在 IE6、IE7 等老浏览器中的应用

    什么是 CSS Reset CSS Reset 是一种通过重置浏览器默认样式的方式,消除不同浏览器之间的差异,从而实现更加一致的样式效果的技术手段。在前端开发中,使用 CSS Reset 可以让我们更...

    2 个月前
  • ES6 中的类继承和原型链之间的关系解析

    在 ES6 中,引入了 class 关键字,使得 JavaScript 也具备了面向对象编程的能力。在类继承和原型链之间,有着密切的关系。本文将详细解析 ES6 中的类继承和原型链之间的关系,并提供一...

    2 个月前
  • 如何使用 Redux 处理 React 应用中的表单数据

    前言 在开发 React 应用时,表单数据的处理是非常常见的需求。然而,由于 React 的单向数据流和组件化特性,传统的表单处理方式可能会变得非常繁琐。而 Redux 作为一种状态管理工具,可以帮助...

    2 个月前
  • Redis 处理高并发的策略

    前言 随着互联网的发展,高并发已经成为了一个不可避免的问题。而 Redis 作为一款高性能的 NoSQL 数据库,也成为了处理高并发的重要工具之一。本文将会介绍 Redis 处理高并发的策略,并且会提...

    2 个月前
  • 响应式设计中的图片适配问题解决方案

    在响应式设计中,图片适配是一个比较棘手的问题。如果不加以处理,可能会导致图片在不同设备上显示不佳,影响用户体验。本文将介绍响应式设计中的图片适配问题,并提供解决方案。

    2 个月前
  • 解析 TypeScript 中 encapsulation(封装)的实现方式

    解析 TypeScript 中 encapsulation(封装)的实现方式 在 TypeScript 中,封装(encapsulation)是一种重要的面向对象编程的特性。

    2 个月前
  • PM2 崩溃处理:如何避免由于 PM2 进程奔溃导致应用崩溃?

    在前端开发中,我们经常使用 PM2 进行进程管理和部署。但是,当 PM2 进程崩溃时,应用也会跟着崩溃。如何避免这种情况的发生?本文将介绍 PM2 崩溃处理的方法和技巧,帮助您更好地管理和部署应用。

    2 个月前
  • 在 Node.js 中运行 HTTPS 服务器的方法

    Node.js 是一个非常流行的 JavaScript 运行时环境,它可以让我们通过 JavaScript 编写服务器端应用程序。在开发 Web 应用程序时,安全性是非常重要的。

    2 个月前
  • 详解 ECMAScript 2018 中的三个新操作符及其用法

    ECMAScript 2018 (简称 ES2018) 是 JavaScript 语言的最新标准,其中包含了许多新特性和语法糖。本文将详细介绍其中的三个新操作符及其用法,分别是:扩展运算符、剩余运算符...

    2 个月前
  • 解决 Enzyme 测试 React Native 组件时动画无法渲染的问题

    在开发 React Native 应用时,我们经常需要使用 Enzyme 来测试组件。然而,当我们测试涉及到动画的组件时,我们可能会遇到一些问题:动画无法渲染,导致测试失败。

    2 个月前
  • 使用 React Router 打造复杂而强大的 SPA 应用

    随着 Web 技术的不断发展,单页应用(Single Page Application,SPA)已经成为了现代 Web 应用的主流。SPA 通过异步加载数据和动态更新页面,提供了更快速、更流畅的用户体...

    2 个月前
  • AngularJS 中如何使用 ng-repeat 中的 filter 来过滤数据

    在 AngularJS 中,ng-repeat 指令是用于循环遍历数组或对象并生成 HTML 元素的常用指令。而 ng-repeat 指令中的 filter 属性则是用于过滤数据的功能。

    2 个月前
  • 如何在 Chai 中验证 Promise.all

    如何在 Chai 中验证 Promise.all 在前端开发中,Promise.all 是一个非常常用的功能,它可以让我们在多个异步操作完成后再执行一些操作,这个功能在实际开发中非常实用。

    2 个月前
  • Mongoose 实现数据批量更新的方式详解

    前言 在前端开发中,经常会涉及到对数据库中的数据进行批量更新的操作。而 Mongoose 是一款 Node.js 平台下的 MongoDB 对象模型工具,它提供了一种方便的方式来操作 MongoDB ...

    2 个月前
  • 在使用 lit-element 的时候,如何解决麻烦的 Shadow DOM 的变量传递问题

    前言 在使用 Web Components 的时候,我们通常会使用 Shadow DOM 来实现封装和样式隔离。然而,Shadow DOM 的封闭性也带来了一些挑战,其中之一就是变量传递问题。

    2 个月前
  • Tailwind CSS 如何实现动态换肤?

    随着互联网的发展,越来越多的网站和应用开始支持动态换肤功能。动态换肤不仅可以提升用户体验,还可以让用户在不同的环境下选择适合自己的主题,增加用户黏性和满意度。本文将介绍如何使用 Tailwind CS...

    2 个月前
  • 如何在 Less 中使用字符串操作函数?

    在前端开发中,样式表是不可或缺的一部分。而 Less 是一种动态样式语言,它是 CSS 的一种扩展。在 Less 中,我们可以使用字符串操作函数来处理字符串,这些函数可以帮助我们更加方便地操作字符串,...

    2 个月前

相关推荐

    暂无文章