JavaScript >> Javascript 文檔 >  >> React

使用 Redux 進行狀態管理的另一種方法

Redux 經常在 React.js 應用程序中用於管理全局狀態。通常,redux 存儲遵循與應用程序的數據庫架構類似的形狀。例如。對於表 tasks , 你通常有一個對應的 tasksReducer .

使用 Redux,您的應用程序數據現在位於兩個位置:前端和後端:

  • 在前端,我們需要有一個中心位置來保持數據的一致性。例如。當我們改變 title task 的屬性 在我們的應用程序中,我們希望此更改直接在顯示此 title 的所有反應組件中可見 屬性。如果沒有 redux,應用程序可能仍會意外顯示舊的 title 其他組件中的屬性。
  • 後端為數據提供實際的單一事實來源。

在構建單頁應用程序時,很多工作都花在使這兩個系統保持同步上: 當你添加一個新的 task 應用程序中的對象,您首先將其添加到您的 redux 商店,因此它在 UI 中可見,您還可以對後端進行 API 調用。當後端 API 調用失敗時,你想再次從你的 redux 存儲中刪除記錄,否則你的本地狀態會與事實來源不同步。

保持這兩個系統(後端和前端)同步從根本上來說是困難的,因為它正在處理分佈式系統問題。

來自多頁應用的靈感

無需為我們項目中的每個表手動編寫此狀態管理代碼,我們可以通過重新思考我們的方法來大大簡化問題。

我們可以從中獲得靈感的一個方向是多頁應用程序。多頁應用程序通常比單頁應用程序簡單得多。多頁面應用程序始終直接在 SQL 數據庫的狀態上呈現。例如。在構建一個簡單的 PHP 應用程序時,您從數據庫中獲取一些數據,然後根據該數據呈現 HTML。沒有像 redux 這樣的第二個系統。這是使多頁面應用程序更易於構建的原因之一。

<?php
// Fetch data
$query = "SELECT * FROM tasks ORDER BY created_at";

$statement = $conn->prepare($query);
$statement->execute();

$tasks = $statement->fetchAll();

// Render HTML
echo "<div>";
echo "<h1>Tasks</h1>";
foreach ($tasks as $task) {
    echo "<div>" . htmlspecialchars($task['title']) . "</div>";
}
echo "</div>";

我們可以將這個原則應用到單頁應用嗎?

讓我們試試吧。

從前端查詢

首先,我們需要一種描述查詢的方法。這可能看起來像這樣:

const theQuery = query('tasks').orderBy('createdAt');

與多頁應用程序不同,在我們的單頁應用程序中,視圖需要在底層數據更改時重新呈現。所以我們還需要一種方法,讓服務器在查詢的底層數據庫記錄發生變化時通知客戶端,以便重新渲染組件。

在 React 中,這通常使用 Hook 來解決。假設我們已經構建了一個自定義 useQuery 每當該鉤子返回的數據庫記錄發生更改時,該鉤子就會神奇地刷新。它看起來像這樣:

function Tasks() {
    // Fetch data
    // and magically keep the data fresh
    const tasks = useQuery(query('tasks').orderBy('createdAt'));

    // Render
    return <div>
        <h1>Tasks</h1>
        {tasks?.map(task => <div>{task.title}</div>)}
    </div>
}

可以看到這個結構跟上面PHP代碼的結構很接近。

useQuery 始終返回最新的數據庫狀態,並在數據庫中的記錄發生更改時自動刷新。有了這個,我們現在實際上已經歸檔了跨應用程序狀態的一致性目標。我們最初打算用 redux 解決的目標。我們現在不是基於 redux 存儲來渲染視圖,而是基於實際的數據庫來渲染視圖。就像好的舊 PHP 一樣。

突變

使用 useQuery 當底層數據更改時自動刷新,我們可以以任何我們想要的方式進行突變。我們可以使用手動 REST Api 調用突變,以及像 createRecord(tableName, record) 這樣的自定義函數 或 updateRecord(tableName, id, patch) ,或微服務。

只要突變寫入數據庫,數據庫更改就會被我們的useQuery拾取 自動。

精簡後端

我們把上述useQuery的API思路 和 query 與 Thin Backend 一起工作。 Thin 為您提供了一種簡單的方法,可以讓您的後端只是數據上的一個薄層,同時在前端提供豐富的交互式體驗。

Thin Backend 提供 useQuery 自動訂閱 Postgres 表中的更改並通知任何 useQuery 的鉤子 關於這些變化的電話。為了確保每個用戶的數據安全和私密,我們使用 Postgres 政策僅在您的政策規定的情況下才授予訪問權限。

Thin 還提供了簡單的函數來創建、更新和刪除數據庫記錄:

const task = await createRecord('tasks', { title: 'New task' });
await updateRecord('tasks', task.id, { title: 'Updated title' });
await deleteRecord('tasks', task.id);

以下是使用這些 API 的簡單待辦事項應用程序的外觀:

import { query, createRecord } from 'thin-backend';
import { useQuery } from 'thin-backend-react';

function Tasks() {
    // `useQuery` always returns the latest records from the db
    const tasks = useQuery(query('tasks').orderBy('createdAt'));

    return <div>
        {tasks.map(task => <Task task={task} key={task.id} />)}
    </div>
}

function Task({ task }) {
    return <div>{task.title}</div>
}

function AddTaskButton() {
    const handleClick = () => {
        const task = { title: window.prompt('Title:') };

        createRecord('tasks', task);
    }

    return <button onClick={handleClick}>Add Task</button>
}

function App() {
    // No need for state management libs
    // `useQuery` automatically triggers a re-render on new data
    return <div>
        <Tasks />
        <AddTaskButton />
    </div>
}

您可以在此處運行該代碼的現場演示。

一旦開始使用上述 API,您將體驗到它可以極大地簡化前端數據庫狀態的管理。最後你甚至可能根本不再需要 redux。

結論

通過基於我們實際的數據庫狀態而不是像 redux 這樣的第二個系統來渲染視圖,我們可以從根本上簡化現代單頁前端的狀態管理。

如果您對此感到好奇,請在 Thin.dev 上嘗試一下。

以下是試用者對 Thin 的評價:


Tutorial JavaScript 教程
  1. 2020 年最佳類似 React 的 JSX UI 庫

  2. 如何像專業人士一樣使用 React useReducer hook

  3. 用 Javascript 編寫函數

  4. Anagrams Checker - 三種 JavaScript 解決方案

  5. 2020 年適用於 React 開發人員的 10 個生產力工具

  6. 翻轉所有卡片,但想一張一張地翻轉

  7. 我如何打造有史以來最好的井字遊戲

  1. 登錄表單

  2. JS 異步:async/await - zh

  3. WebdriverIO - 上傳文件示例

  4. 如何退出 Node.js 程序

  5. 100DaysOfCode - 終於接受挑戰💪

  6. 從書籤加載外部 JS?

  7. 通過玩遊戲學習 JavaScript 編碼🎮

  1. Facebooks Graph API 的第一印象

  2. 使用 GatsbyJS 實現順風 CSS 的 7 個步驟

  3. 在 Flutter 中創建多頁面應用

  4. 如何創建 Angular 應用程序:使用 Kendo UI 添加樣式