【React】将 useReducer 应用于 Web Worker,擦出奇妙的火花

837 阅读5分钟

我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛

有这么一个场景,当加载一个网页时,它突然变得无响应,直到所有的资源完全加载完毕才响应。但是,当资源加载时,用户可能无法执行页面上的某些功能,比如单击、选择或拖动元素。这个时候你可能会想到使用 Web workers帮助我们解决这个问题。 在本文中,我们将学习如何在 React 应用程序中使用web workers。我们还将学习通过 useWorkerizedReducer 在web worker 中使用 useReducer Hook。

web worker

web worker 是一个JavaScript脚本,它在后台运行,不会干扰其他脚本的执行。

因为JavaScript是单线程语言,它不能同时运行多个脚本,这对于运行大型计算脚本来说是一个问题。Web worker帮助在后台加载繁重的计算脚本,而不会影响页面的性能。

语法

const worker = new Worker(new URL("./worker.js", import.meta.url), {
 type: "module",
});

Worker 构造函数接受两个参数;第一个是 worker 文件名,第二个是worker 的类型。类型可以是 classic,或者是 module。如果未指定类型,则默认类型为 classic

在本文中,我们将使用module类型,因为 reducer 只能在组件中使用。要在 web worker 组件中使用 import 函数,我们必须将import.meta. URL 添加到 URL 构造函数中。

useReducer

useReducer是一个React Hook,用于存储和更新状态。它接受三个参数:reducer, initial state,作为最后一个参数,initial function,它是可选的:

const [state, dispatch] = useReducer(reducer, initialArg, init);

useReducer 返回一个包含当前 state 值的数组,以及一个 dispatch 函数,你可以向该 dispatch 函数提供要执行的操作。

dispatch 函数接受指定要执行的操作类型的对象。它本质上是将 action 的类型传递给 reducer 函数,而 reducer 函数用于更新 state

reducer 函数

reducer 是一个接受两个参数的函数,当前 stateaction 对象。它使用接收到的 action 来确定 state 的更改并返回新 state

下面的代码演示了如何使用 reducer 函数来改变 state:

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};

   default:
      throw new Error();
  }
}

每当 reducer 函数被 action.type 触发时,它就会将新的 state 作为 reducer 函数内部的更改返回。

action 是一种对象类型,它指示 reducer 如何更改 state。它必须具有 type 属性。可以在条件语句中使用 action.type 来决定 state 如何更改。

为了让 reducer 函数在 web worker 中运行,我们必须使用useWorkerizedReducer

useWorkerizedReducer

useWorkerizedReducer 类似于 useReducer,除了它允许 reducer 在 worker 中执行,还允许我们创建一个动态的 React 应用。

useWorkerizedReducer 允许在不影响应用程序响应的情况下将长时间运行的计算放置在 reducer 中。useWorkerizedReducer 负责向 worker 提供 useReducer 的功能。

通过将 reducerstate 复制到主线程,useWorkerizedReducer 在工作线程和主线程之间架起了桥梁。reducer 操作 worker 的 state 对象,使用 postMessage() 来保持复制主线程的当前状态。

实战:构建一个简单的计数器应用程序

为了学习如何在web worker中放置 Reducer,让我们创建一个简单的计数器程序,它将在当前 state 发生改变时返回。

首先,打开命令行,输入以下命令:

npx create-react-app my-app
cd my-app
npm start

在成功安装应用程序之后,我们需要将 useWorkerizedReducer 作为程序的依赖项来使用它。安装 “useWorkerizedReducer”,在终端中执行如下命令:

npm i use-workerized-reducer

现在我们已经成功安装了useWorkerizedReducer,让我们接着创建一个worker.js 文件。

创建 worker.js

因为我们在 worker.js 文件中使用了 reducer,所以我们将在 src 文件夹中创建 worker.js 文件: 单击“创建新文件”,将其命名为 worker.js,然后将其保存到 src 文件夹中,如下所示:

img

现在我们已经创建了 worker.js 文件,让我们在其中添加下面的 reducer 代码:

// worker.js
import { initWorkerizedReducer } from "use-workerized-reducer";

initWorkerizedReducer(
 "counter", //  reducer's name
 async (state, action) => {
   switch (action.type) {
     case "increment":
       state.counter += 1;
       break;
     case "decrement":
       state.counter -= 1;
       break;
     default:
       throw new Error();
   }
 }
);

在上面的代码中,我们从 use-workerizedreducer 中导入了initWorkerizedReducer

initWorkerizedReducer() 接受两个参数:

  • 第一个是 reducer 的名称:counter。
  • 第二个是一个异步函数。

现在我们已经准备好了 worker.js 文件,我们需要从 use- workerizedreducer /react 中导入 useWorkerizedReducer ,这让我们可以从 worker 文件中调用 reducer 函数:

// main.js
import { render, h, Fragment } from "react";
import { useWorkerizedReducer } from "use-workerized-reducer/react";

const worker = new Worker(new URL("./worker.js", import.meta.url), {
 type: "module",
});

function App() {
 // 一个 worker 可以包含多个不同名的 reducer

 const [state, dispatch, busy] = useWorkerizedReducer(
   worker,
   "counter", // Reducer name
   { counter: 0 } // Initial state
 );

 return (
   <>
     Count: {state.counter}
     <button disabled={busy} onClick={() => dispatch({ type: "decrement" })}>
       -
     </button>
     <button disabled={busy} onClick={() => dispatch({ type: "increment" })}>
       +
     </button>
   </>
 );
}

export default App;

useWorkerizedReducer 函数接受三个参数并返回单个值:

  • 当前state是第一个参数;
  • action 是第二个参数;
  • 初始状态是第三个参数。

处理的数据是 statedispatch 函数执行传递给 reducer 函数的action

Busy将一直为 true,直到 worker 的初始状态 counter 成功复制到 worker。在此之后,如果 actions 仍在处理中,则 Busy 返回 true,否则返回 false。

reducer 根据 action 类型改变状态。action 类型 increment, decrement和reset都是在 dispatch 时更新 state 的 action 类型。

初始状态为{counter: 0}。当递增操作类型被 dispatch 时,我们简单地设置 count {state.Count + 1}

结尾

在这篇文章中,我们简要介绍了 web worker 和 useReducer,以及如何构造和添加 web worker 文件到 React 应用程序中。

我们还讨论了useWorkerizedReducer,它为 web worker 带来了useReducer 的功能。最后,我们介绍了在useWorkerizedReducer的帮助下在 web worker中使用 reducer。