在 React 中用事件驅動進行狀態管理

做者:Abdulazeez Abdulazeez Adeshina

翻譯:瘋狂的技術宅javascript

原文:https://blog.logrocket.com/ev...css

未經容許嚴禁轉載html

自 Hook 被引入 React 以來,Context API 與 Hook 庫在應用狀態管理中被一塊兒使用。可是把 Context API 和 Hooks(許多基於 Hooks 的狀態管理庫創建在其基礎上)組合的用法對於大規模應用來講可能效率不高。前端

因爲必須建立一個自定義的 Hook 才能啓用對狀態及其方法的訪問,而後才能在組件中使用它,因此在實際開發中很繁瑣。這違反了 Hook 的真正目的:簡單。可是對於較小的應用,Redux 可能會顯得過重了。java

今天咱們將討論 Context API 的替代方法:Storeon。 Storeon 是一個微型的、事件驅動的 React 狀態管理庫,其原理相似於 Redux。用 Redux DevTools 能夠查看並可視化狀態操做。 Storeon 內部使用 Context API 來管理狀態,並採用事件驅動的方法進行狀態操做。react

Store

store 是在應用程序狀態下存儲的數據的集合。它是經過從 Storeon 庫導入的 createStoreon() 函數建立的。git

createStoreon() 函數接受模塊列表,其中每一個模塊都是一個接受 store 參數並綁定其事件監聽器的函數。這是一個store 的例子:程序員

import { createStoreon } from 'storeon/react'
// todos module
const todos = store => {
  store.on(event, callback)
}

export default const store = createStoreon([todos])

模塊化

Storeon 中的 store 是模塊化的,也就是說,它們是獨立定義的,而且沒有被綁定到 Hook 或組件。每一個狀態及其操做方法均在被稱爲模塊的函數中定義。這些模塊被傳遞到 createStoreon() 函數中,而後將其註冊爲全局 store。github

store 有三種方法:面試

  1. store.get() – 用於檢索狀態中的當前數據。
  2. store.on(event, callback) – 用於把事件偵聽器註冊到指定的事件名稱。
  3. store.dispatch(event, data) – 用於發出事件,並根據定義的事件要求將可選數據傳遞進來。

Events

Storeon 是基於事件的狀態管理庫,狀態更改由狀態模塊中定義的事件發出。 Storeon 中有三個內置事件,它們以 @ 開頭。其餘事件不帶 @ 前綴定義。三個內置事件是:

  1. @init – 在應用加載時觸發此事件。它用於設置應用的初始狀態,並執行傳遞給它的回調中的全部內容。
  2. @dispatch – 此事件在每一個新動做上觸發。這對於調試頗有用。
  3. @changed – 當應用狀態發生更改時,將觸發此事件。
注意store.on(event,callback) 用於在咱們的模塊中添加事件監聽器。

演示程序

爲了演示在 Storeon 中如何執行應用程序狀態操做,咱們將構建一個簡單的 notes 程序。還會用 Storeon 的另外一個軟件包把狀態數據保存在 localStorage 中。

假設你具備 JavaScript 和 React 的基本知識。你能夠在 https://github.com/Youngestde... 上找到本文中使用的代碼。

設置

在深刻探討以前,讓咱們先勾勒出 Notes 程序所需的項目結構和依賴項的安裝。從建立項目文件夾開始。

mkdir storeon-app && cd storeon-app
mkdir {src,public,src/Components}
touch public/{index.html, style.css} && touch src/{index,store,Components/Notes}.js

接下來,初始化目錄並安裝所需的依賴項。

npm init -y
npm i react react-dom react-scripts storeon @storeon/localstorage uuidv4

接下來就是在 index.js文件中編寫父組件了。

index.js

這個文件負責渲染咱們的筆記組件。首先導入所需的包。

import React from 'react'
import { render } from 'react-dom';

function App() {
  return (
    <>
      Hello!
    </>
  );
}
const root = document.getElementById('root');
render(<App />, root);

接下來經過在 store.js 中編寫用於狀態的初始化和操做的代碼來構建 store。

store.js

此文件負責處理應用中的狀態和後續狀態管理操做。咱們必須建立一個模塊來存儲狀態以及支持事件,以處理操做變動。

首先,從 Storeon 導入 createStoreon 方法和惟一隨機ID生成器 UUID。

createStoreon 方法負責將咱們的 狀態 註冊到全局 store 。

import { createStoreon } from 'storeon';
import { v4 as uuidv4 } from 'uuid'
import { persistState } from '@storeon/localstorage';

let note = store => {}

咱們將狀態存儲在數組變量 notes 中,該變量包含如下格式的註釋:

{
  id: 'note id',
  item: 'note item'
},

接下來,咱們將用兩個註釋(在首次啓動程序時會顯示)來初始化狀態,從而首先填充註釋模塊。而後,定義狀態事件。

let note = store => {
  store.on('@init', () => ({
    notes: [
      { id: uuidv4(), item: 'Storeon is a React state management library and unlike other state management libraries that use Context, it utilizes an event-driven approach like Redux.' },
      { id: uuidv4(), item: 'This is a really short note. I have begun to study the basic concepts of technical writing and I'\'m optimistic about becoming one of the best technical writers.' },
    ]
  });
  store.on('addNote', ({ notes }, note) => {
    return {
      notes: [...notes, { id: uuidv4(), item: note }],
    }
  });
  store.on('deleteNote', ({ notes }, id) => ({
    notes: notes.filter(note => note.id !== id),
  });
}

在上面的代碼中,咱們定義了狀態,並用兩個簡短的註釋填充了狀態,並定義了兩個事件和一個從 dispatch(event, data) 函數發出事件後將會執行的回調函數。

addNote 事件中,咱們返回添加了新 note 的更新後的狀態對象,在 deleteNote 事件中把 ID 傳遞給調度方法的 note 過濾掉。

最後,把模塊分配給可導出變量 store ,將其註冊爲全局 store,以便稍後將其導入到上下文 provider 中,並將狀態存儲在 localStorage 中。

const store = createStoreon([
  notes,
  // Store state in localStorage
  persistState(['notes']),
]);
export default store;

接下來,在 Notes.js 中編寫 Notes 應用組件。

Notes.js

此文件包含 Notes 程序的組件。咱們將從導入依賴項開始。

import React from 'react';
import { useStoreon } from 'storeon/react';

接下來,編寫組件。

const Notes = () => {
  const { dispatch, notes } = useStoreon('notes');
  const [ value, setValue ] = React.useState(''); 
}

在上面的代碼的第二行中,useStoreon() hook 的返回值設置爲可破壞的對象。 useStoreon() hook 使用模塊名稱做爲其參數,並返回狀態和調度方法以發出事件。

接下來定義在組件中發出狀態定義事件的方法 。

const Notes = () => {
...
  const deleteNote = id => {
    dispatch('deleteNote', id)
  };
  const submit = () => {
    dispatch('addNote', value);
    setValue('');
  };
  const handleInput = e => {
    setValue(e.target.value);
  };
}

Let’s review the three methods we defined the above:
讓咱們回顧一下上面定義的三種方法:

  1. deleteNote(id) – 此方法在觸發時調度 deleteNote 事件。
  2. submit() – 該方法經過傳遞輸入狀態的值來調度 addNote 事件,該狀態在 Notes 組件中本地定義。
  3. handleInput() – 此方法將本地狀態的值設置爲用戶輸入。

Next, we’ll build the main interface of our app and export it.
接下來,咱們將構建應用程序的主界面並將其導出。

const Notes = () => {
  ...
  return (
    <section>
      <header>Quick Notes</header>

      <div className='addNote'>
        <textarea onChange={handleInput} value={value} />
        <button onClick={() => submit()}> Add A Note </button>
      </div>

      <ul>
        {notes.map(note => (
          <li key={note.id}>
            <div className='todo'>
              <p>{note.item}</p>
              <button onClick={() => deleteNote(note.id)}>Delete note</button>
            </div>
          </li>
        ))}
      </ul>
    </section>
  );
}

這樣就構成了咱們的 Notes 組件。接下來爲咱們的應用和 index.html 文件編寫樣式表。

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="style.css">
    <title>Storeon Todo App</title>
</head>

<body>
    <div id="root"></div>
</body>

</html>

接下來,填充咱們的 style.css 文件。

style.css

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

section {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  width: 300px;
  margin: auto;
}

header {
  text-align: center;
  font-size: 24px;
  line-height: 40px;
}

ul {
  display: block;
}

.todo {
  display: block;
  margin: 12px 0;
  width: 300px;
  padding: 16px;
  box-shadow: 0 8px 12px 0 rgba(0, 0, 0, 0.3);
  transition: 0.2s;
  word-break: break-word;
}

li {
  list-style-type: none;
  display: block;
}

textarea {
  border: 1px double;
  box-shadow: 1px 1px 1px #999;
  height: 100px;
  margin: 12px 0;
  width: 100%;
  padding: 5px 10px;
}

button {
  margin: 8px 0;
  border-radius: 5px;
  padding: 10px 25px;
}

.box:hover {
  box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}

運行

如今咱們已經成功編寫了組件和樣式表,可是尚未更新 index.js 中的父組件來渲染 Notes 組件。接下來讓咱們渲染 Notes 組件。

index.js

要訪問咱們的全局 store,必須導入 store 和 Storeon store 上下文組件。咱們還將導入 notes 組件來進行渲染。

用如下代碼替換組件的內容:

import React from 'react';
import { render } from 'react-dom';
import { StoreContext } from 'storeon/react';
import Notes from './Components/Notes';
import store from '../src/store';

function App() {
  return (
    <>
      <StoreContext.Provider value={store}>
        <Notes />
      </StoreContext.Provider>
    </>
  );
}

const root = document.getElementById('root');
render(<App />, root);

在第 8-10 行,調用 store 上下文提供程序組件,並將 notes 組件做爲使用者傳遞。store 上下文提供程序組件將全局 store 做爲其上下文值。

接下來把 package.json 文件中的腳本部分編輯爲如下內容:

"scripts": {
  "start": "react-scripts start",
}

而後運行咱們的程序:

npm run start

讓咱們繼續添加和刪除註釋:

Adding And Deleting Notes In Our App

Storeon devtools

Storeon 與 Redux 有着類似的屬性,能夠在 Redux DevTools 中可視化和監視狀態的更改。爲了可視化 Storeon 程序中的狀態,咱們將導入 devtools 包,並將其做爲參數添加到咱們 store.js 文件的 createStoreon() 方法中。

...
import { storeonDevtools } from 'storeon/devtools';
...
const store = createStoreon([
  ...,
  process.env.NODE_ENV !== 'production' && storeonDevtools,
]);

這是用 Redux DevTools 可視化狀態變化的演示:

用Redux DevTools可視化咱們程序中的狀態更改

結論

Storeon 是一個很是有用的狀態管理庫,它用事件驅動和 Redux 改編的模塊化樣式來管理狀態。你能夠在 https://github.com/Youngestde... 上找到本文中的代碼。


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索