× Giới thiệu Lịch khai giảng Tin tức Sản phẩm học viên

ReactJS - Redux thư viên quản lý trạng thái nâng cao

06/04/2023 01:24

React redux là một thư viện quản lý trạng thái nâng cao dành cho React. Như chúng ta đã biết trước đó, React chỉ hỗ trợ quản lý trạng thái cấp thành phần. Trong một ứng dụng lớn và phức tạp, số lượng lớn các thành phần được sử dụng.

React redux là một thư viện quản lý trạng thái nâng cao dành cho React. Như chúng ta đã biết trước đó, React chỉ hỗ trợ quản lý trạng thái cấp thành phần. Trong một ứng dụng lớn và phức tạp, số lượng lớn các thành phần được sử dụng. React khuyên bạn nên chuyển trạng thái sang thành phần cấp cao nhất và chuyển trạng thái sang thành phần lồng nhau bằng các thuộc tính. Nó giúp ở một mức độ nào đó nhưng nó trở nên phức tạp khi các thành phần tăng lên.

Phản ứng các chip redux trong và giúp duy trì trạng thái ở cấp ứng dụng. React redux cho phép bất kỳ thành phần nào truy cập trạng thái bất kỳ lúc nào. Ngoài ra, nó cho phép bất kỳ thành phần nào thay đổi trạng thái của ứng dụng bất kỳ lúc nào.

Hãy để chúng tôi tìm hiểu về cách viết ứng dụng React bằng cách sử dụng React redux trong chương này.

Các khái niệm

React redux duy trì trạng thái của ứng dụng ở một nơi duy nhất gọi là Redux store. Thành phần phản ứng có thể lấy trạng thái mới nhất từ ​​cửa hàng cũng như thay đổi trạng thái bất kỳ lúc nào. Redux cung cấp một quy trình đơn giản để nhận và thiết lập trạng thái hiện tại của ứng dụng và bao gồm các khái niệm bên dưới.

Cửa hàng - Vị trí trung tâm để lưu trữ trạng thái của ứng dụng.

Hành động - Hành động là một đối tượng đơn giản với loại hành động được thực hiện và đầu vào (được gọi là tải trọng) cần thiết để thực hiện hành động. Ví dụ: hành động thêm một mặt hàng trong cửa hàng chứa ADD_ITEM dưới dạng loại và một đối tượng có chi tiết của mặt hàng dưới dạng trọng tải. Hành động có thể được biểu diễn dưới dạng -

{ 
   type: 'ADD_ITEM', 
   payload: { name: '..', ... }
}

Bộ giảm tốc - Bộ giảm tốc là các hàm thuần túy được sử dụng để tạo trạng thái mới dựa trên trạng thái hiện có và hành động hiện tại. Nó trả về trạng thái mới được tạo. Ví dụ: trong trường hợp thêm mục, nó tạo danh sách mục mới và hợp nhất mục từ trạng thái với mục mới và trả về danh sách mới được tạo.

Trình tạo hành động − Trình tạo hành động tạo một hành động với loại hành động thích hợp và dữ liệu cần thiết cho hành động đó và trả về hành động đó. Ví dụ: trình tạo hành động addItem trả về đối tượng bên dưới -

{ 
   type: 'ADD_ITEM', 
   payload: { name: '..', ... }
}

Thành phần − Thành phần có thể kết nối với cửa hàng để lấy trạng thái hiện tại và gửi hành động đến cửa hàng để cửa hàng thực thi hành động và cập nhật trạng thái hiện tại của nó

  • Thành phần phản ứng đăng ký vào cửa hàng và nhận trạng thái mới nhất trong quá trình khởi tạo ứng dụng.
  • Để thay đổi trạng thái, thành phần React tạo hành động cần thiết và gửi hành động.
  • Reducer tạo một trạng thái mới dựa trên hành động và trả về nó. Cửa hàng tự cập nhật với trạng thái mới.
  • Khi trạng thái thay đổi, cửa hàng sẽ gửi trạng thái đã cập nhật tới tất cả thành phần đã đăng ký của nó.

Redux API

Redux cung cấp một api duy nhất, kết nối sẽ kết nối một thành phần với cửa hàng và cho phép thành phần đó nhận và đặt trạng thái của cửa hàng.

Chữ ký của API kết nối là -

function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)

Tất cả các tham số là tùy chọn và nó trả về một HOC (thành phần bậc cao hơn). Thành phần bậc cao hơn là một hàm bao bọc một thành phần và trả về một thành phần mới.

let hoc = connect(mapStateToProps, mapDispatchToProps) 
let connectedComponent = hoc(component)

Chúng ta hãy xem hai tham số đầu tiên sẽ đủ cho hầu hết các trường hợp.

  • mapStateToProps − Chấp nhận một chức năng có chữ ký bên dưới.

(state, ownProps?) => Object

Ở đây, trạng thái đề cập đến trạng thái hiện tại của cửa hàng và Đối tượng đề cập đến các đạo cụ mới của thành phần. Nó được gọi bất cứ khi nào trạng thái của cửa hàng được cập nhật.

(state) => { prop1: this.state.anyvalue }
  • mapDispatchToProps − Chấp nhận một chức năng có chữ ký bên dưới.

Object | (dispatch, ownProps?) => Object

Ở đây, công văn đề cập đến đối tượng công văn được sử dụng để gửi hành động trong kho lưu trữ redux và Đối tượng đề cập đến một hoặc nhiều hàm điều phối làm đạo cụ của thành phần.

(dispatch) => {
   addDispatcher: (dispatch) => dispatch({ type: 'ADD_ITEM', payload: { } }),
   removeispatcher: (dispatch) => dispatch({ type: 'REMOVE_ITEM', payload: { } }),
}

Thành phần nhà cung cấp

React Redux cung cấp một thành phần Nhà cung cấp và mục đích duy nhất của nó là cung cấp cửa hàng Redux cho tất cả các thành phần lồng nhau được kết nối với cửa hàng bằng API kết nối. Mã mẫu được đưa ra dưới đây -

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { App } from './App'
import createStore from './createReduxStore'

const store = createStore()

ReactDOM.render(
   <Provider store={store}>
      <App />
   </Provider>,
   document.getElementById('root')
)

Bây giờ, tất cả thành phần bên trong thành phần Ứng dụng có thể truy cập vào cửa hàng Redux bằng cách sử dụng API kết nối.

ví dụ làm việc

Hãy để chúng tôi tạo lại ứng dụng quản lý chi phí của mình và sử dụng khái niệm React redux để duy trì trạng thái của ứng dụng.

Trước tiên, hãy tạo một ứng dụng phản ứng mới, ứng dụng thông báo phản ứng bằng cách sử dụng Tạo ứng dụng phản ứng hoặc gói Tổng số bằng cách làm theo hướng dẫn trong chương Tạo ứng dụng React.

Tiếp theo, cài đặt thư viện Redux và React redux.

npm install redux react-redux --save

Tiếp theo, cài đặt thư viện uuid để tạo mã định danh duy nhất cho các chi phí mới.

npm install uuid --save

Tiếp theo, mở ứng dụng trong trình chỉnh sửa yêu thích của bạn.

Tiếp theo, tạo thư mục src trong thư mục gốc của ứng dụng.

Tiếp theo, tạo thư mục hành động trong thư mục src .

Tiếp theo, tạo một tệp, type.js trong thư mục src/actions và bắt đầu chỉnh sửa.

Tiếp theo, thêm hai loại hành động, một để thêm chi phí và một để loại bỏ chi phí.

export const ADD_EXPENSE = 'ADD_EXPENSE'; 
export const DELETE_EXPENSE = 'DELETE_EXPENSE';

Tiếp theo, tạo một tệp, index.js trong thư mục src/actions để thêm hành động và bắt đầu chỉnh sửa.

Tiếp theo, nhập uuid để tạo mã định danh duy nhất.

import { v4 as uuidv4 } from 'uuid';

Tiếp theo, nhập các loại hành động.

import { ADD_EXPENSE, DELETE_EXPENSE } from './types';

Tiếp theo, thêm một chức năng mới để trả về loại hành động để thêm chi phí và xuất nó.

export const addExpense = ({ name, amount, spendDate, category }) => ({
   type: ADD_EXPENSE,
   payload: {
      id: uuidv4(),
      name,
      amount,
      spendDate,
      category
   }
});

Ở đây, hàm mong đợi đối tượng chi phí và trả về loại hành động ADD_EXPENSE cùng với tải trọng thông tin chi phí.

Tiếp theo, thêm một chức năng mới để trả về loại hành động để xóa một khoản chi phí và xuất nó.

export const deleteExpense = id => ({
   type: DELETE_EXPENSE,
   payload: {
      id
   }
});

Tại đây, hàm dự kiến ​​id của mục chi phí sẽ bị xóa và trả về loại hành động là 'DELETE_EXPENSE' cùng với tải trọng của id chi phí.

Mã nguồn hoàn chỉnh của hành động được đưa ra dưới đây -

import { v4 as uuidv4 } from 'uuid';
import { ADD_EXPENSE, DELETE_EXPENSE } from './types';

export const addExpense = ({ name, amount, spendDate, category }) => ({
   type: ADD_EXPENSE,
   payload: {
      id: uuidv4(),
      name,
      amount,
      spendDate,
      category
   }
});
export const deleteExpense = id => ({
   type: DELETE_EXPENSE,
   payload: {
      id
   }
});

Tiếp theo, tạo một thư mục mới, bộ giảm tốc trong thư mục src .

Tiếp theo, tạo một tệp, index.js trong src/reducers để viết hàm giảm tốc và bắt đầu chỉnh sửa.

Tiếp theo, nhập các loại hành động.

import { ADD_EXPENSE, DELETE_EXPENSE } from '../actions/types';

Tiếp theo, thêm một chức năng, CostReducer để thực hiện tính năng thực tế là thêm và cập nhật chi phí trong cửa hàng redux.

export default function expensesReducer(state = [], action) {
   switch (action.type) {
      case ADD_EXPENSE:
         return [...state, action.payload];
      case DELETE_EXPENSE:
         return state.filter(expense => expense.id !== action.payload.id);
      default:
         return state;
   }
}

Mã nguồn hoàn chỉnh của bộ giảm tốc được đưa ra bên dưới -

import { ADD_EXPENSE, DELETE_EXPENSE } from '../actions/types';

export default function expensesReducer(state = [], action) {
   switch (action.type) {
      case ADD_EXPENSE:
         return [...state, action.payload];
      case DELETE_EXPENSE:
         return state.filter(expense => expense.id !== action.payload.id);
      default:
         return state;
   }
}

Tại đây, bộ giảm tốc kiểm tra loại hành động và thực thi mã có liên quan.

Tiếp theo, tạo thư mục thành phần trong thư mục src .

Tiếp theo, tạo một tệp ExpenseEntryItemList.css trong thư mục src/components và thêm kiểu chung cho các bảng html.

html {
   font-family: sans-serif;
}
table {
   border-collapse: collapse;
   border: 2px solid rgb(200,200,200);
   letter-spacing: 1px;
   font-size: 0.8rem;
}
td, th {
   border: 1px solid rgb(190,190,190);
   padding: 10px 20px;
}
th {
   background-color: rgb(235,235,235);
}
td, th {
   text-align: left;
}
tr:nth-child(even) td {
   background-color: rgb(250,250,250);
}
tr:nth-child(odd) td {
   background-color: rgb(245,245,245);
}
caption {
   padding: 10px;
}
tr.highlight td { 
   background-color: #a6a8bd;
}

Tiếp theo, tạo một tệp ExpenseEntryItemList.js trong thư mục src/components và bắt đầu chỉnh sửa.

Tiếp theo, nhập thư viện React và React redux.

import React from 'react'; 
import { connect } from 'react-redux';

Tiếp theo, nhập tệp ExpenseEntryItemList.css.

import './ExpenseEntryItemList.css';

Tiếp theo, nhập trình tạo hành động.

import { deleteExpense } from '../actions'; 
import { addExpense } from '../actions';

Tiếp theo, tạo một lớp ExpenseEntryItemList và gọi hàm tạo bằng props .

class ExpenseEntryItemList extends React.Component {
   constructor(props) {
      super(props);
   }
}

Tiếp theo, tạo hàm mapStateToProps .

const mapStateToProps = state => {
   return {
      expenses: state
   };
};

Ở đây, chúng tôi đã sao chép trạng thái đầu vào vào các đạo cụ chi phí của thành phần.

Tiếp theo, tạo hàm mapDispatchToProps .

const mapDispatchToProps = dispatch => {
   return {
      onAddExpense: expense => {
         dispatch(addExpense(expense));
      },
      onDelete: id => {
         dispatch(deleteExpense(id));
      }
   };
};

Ở đây, chúng tôi đã tạo hai chức năng, một để gửi chức năng thêm chi phí (addExpense) và một chức năng khác để gửi chức năng xóa chi phí (deleteExpense) và ánh xạ các chức năng đó tới các đạo cụ của thành phần.

Tiếp theo, xuất thành phần bằng cách sử dụng kết nối api.

export default connect(
   mapStateToProps,
   mapDispatchToProps
)(ExpenseEntryItemList);

Bây giờ, thành phần có ba thuộc tính mới được cung cấp dưới đây -

  • chi phí - danh sách chi phí

  • onAddExpense - chức năng gửi chức năng addExpense

  • onDelete - hàm gửi hàm deleteExpense

Tiếp theo, thêm một số chi phí vào kho lưu trữ redux trong hàm tạo bằng thuộc tính onAddExpense .

if (this.props.expenses.length == 0)
{
   const items = [
      { id: 1, name: "Pizza", amount: 80, spendDate: "2020-10-10", category: "Food" },
      { id: 2, name: "Grape Juice", amount: 30, spendDate: "2020-10-12", category: "Food" },
      { id: 3, name: "Cinema", amount: 210, spendDate: "2020-10-16", category: "Entertainment" },
      { id: 4, name: "Java Programming book", amount: 242, spendDate: "2020-10-15", category: "Academic" },
      { id: 5, name: "Mango Juice", amount: 35, spendDate: "2020-10-16", category: "Food" },
      { id: 6, name: "Dress", amount: 2000, spendDate: "2020-10-25", category: "Cloth" },
      { id: 7, name: "Tour", amount: 2555, spendDate: "2020-10-29", category: "Entertainment" },
      { id: 8, name: "Meals", amount: 300, spendDate: "2020-10-30", category: "Food" },
      { id: 9, name: "Mobile", amount: 3500, spendDate: "2020-11-02", category: "Gadgets" },
      { id: 10, name: "Exam Fees", amount: 1245, spendDate: "2020-11-04", category: "Academic" }
   ]
   items.forEach((item) => {
      this.props.onAddExpense(
         { 
            name: item.name, 
            amount: item.amount, 
            spendDate: item.spendDate, 
            category: item.category 
         }
      );
   })
}

Tiếp theo, thêm trình xử lý sự kiện để xóa mục chi phí bằng id chi phí.

handleDelete = (id,e) => {
   e.preventDefault();
   this.props.onDelete(id);
}

Tại đây, trình xử lý sự kiện gọi trình điều phối onDelete , hàm này gọi hàm deleteExpense cùng với id chi phí.

Tiếp theo, thêm một phương pháp để tính tổng số tiền của tất cả các chi phí.

getTotal() {
   let total = 0;
   for (var i = 0; i < this.props.expenses.length; i++) {
      total += this.props.expenses[i].amount
   }
   return total;
}

Tiếp theo, thêm phương thức render() và liệt kê mục chi phí ở định dạng bảng.

render() {
   const lists = this.props.expenses.map(
      (item) =>
      <tr key={item.id}>
         <td>{item.name}</td>
         <td>{item.amount}</td>
         <td>{new Date(item.spendDate).toDateString()}</td>
         <td>{item.category}</td>
         <td><a href="#"
            onClick={(e) => this.handleDelete(item.id, e)}>Remove</a></td>
      </tr>
   );
   return (
      <div>
         <table>
            <thead>
               <tr>
                  <th>Item</th>
                  <th>Amount</th>
                  <th>Date</th>
                  <th>Category</th>
                  <th>Remove</th>
               </tr>
            </thead>
            <tbody>
               {lists}
               <tr>
                  <td colSpan="1" style={{ textAlign: "right" }}>Total Amount</td>
                  <td colSpan="4" style={{ textAlign: "left" }}>
                     {this.getTotal()}
                  </td>
               </tr>
            </tbody>
         </table>
      </div>
   );
}

Ở đây, chúng tôi đặt trình xử lý sự kiện handleDelete để xóa chi phí khỏi cửa hàng.

Mã nguồn hoàn chỉnh của thành phần ExpenseEntryItemList được cung cấp dưới đây -

import React from 'react';
import { connect } from 'react-redux';
import './ExpenseEntryItemList.css';
import { deleteExpense } from '../actions';
import { addExpense } from '../actions';

class ExpenseEntryItemList extends React.Component {
   constructor(props) {
      super(props);

      if (this.props.expenses.length == 0){
         const items = [
            { id: 1, name: "Pizza", amount: 80, spendDate: "2020-10-10", category: "Food" },
            { id: 2, name: "Grape Juice", amount: 30, spendDate: "2020-10-12", category: "Food" },
            { id: 3, name: "Cinema", amount: 210, spendDate: "2020-10-16", category: "Entertainment" },
            { id: 4, name: "Java Programming book", amount: 242, spendDate: "2020-10-15", category: "Academic" },
            { id: 5, name: "Mango Juice", amount: 35, spendDate: "2020-10-16", category: "Food" },
            { id: 6, name: "Dress", amount: 2000, spendDate: "2020-10-25", category: "Cloth" },
            { id: 7, name: "Tour", amount: 2555, spendDate: "2020-10-29", category: "Entertainment" },
            { id: 8, name: "Meals", amount: 300, spendDate: "2020-10-30", category: "Food" },
            { id: 9, name: "Mobile", amount: 3500, spendDate: "2020-11-02", category: "Gadgets" },
            { id: 10, name: "Exam Fees", amount: 1245, spendDate: "2020-11-04", category: "Academic" }
         ]
         items.forEach((item) => {
            this.props.onAddExpense(
               { 
                  name: item.name, 
                  amount: item.amount, 
                  spendDate: item.spendDate, 
                  category: item.category 
               }
            );
         })
      }
   }
   handleDelete = (id,e) => {
      e.preventDefault();
      this.props.onDelete(id);
   }
   getTotal() {
      let total = 0;
      for (var i = 0; i < this.props.expenses.length; i++) {
         total += this.props.expenses[i].amount
      }
      return total;
   }
   render() {
      const lists = this.props.expenses.map((item) =>
         <tr key={item.id}>
            <td>{item.name}</td>
            <td>{item.amount}</td>
            <td>{new Date(item.spendDate).toDateString()}</td>
            <td>{item.category}</td>
            <td><a href="#"
               onClick={(e) => this.handleDelete(item.id, e)}>Remove</a></td>
         </tr>
      );
      return (
         <div>
            <table>
               <thead>
                  <tr>
                     <th>Item</th>
                     <th>Amount</th>
                     <th>Date</th>
                     <th>Category</th>
                     <th>Remove</th>
                  </tr>
               </thead>
               <tbody>
                  {lists}
                  <tr>
                     <td colSpan="1" style={{ textAlign: "right" }}>Total Amount</td>
                     <td colSpan="4" style={{ textAlign: "left" }}>
                        {this.getTotal()}
                     </td>
                  </tr>
               </tbody>
            </table>
         </div>
      );
   }
}
const mapStateToProps = state => {
   return {
      expenses: state
   };
};
const mapDispatchToProps = dispatch => {
   return {
      onAddExpense: expense => {
         dispatch(addExpense(expense));
      },
      onDelete: id => {
         dispatch(deleteExpense(id));
      }
   };
};
export default connect(
   mapStateToProps,
   mapDispatchToProps
)(ExpenseEntryItemList);

Tiếp theo, tạo một tệp, App.js trong thư mục src/components và sử dụng thành phần ExpenseEntryItemList .

import React, { Component } from 'react';
import ExpenseEntryItemList from './ExpenseEntryItemList';

class App extends Component {
   render() {
      return (
         <div>
            <ExpenseEntryItemList />
         </div>
      );
   }
}
export default App;

Tiếp theo, tạo một tệp, index.js trong thư mục src.

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './reducers';
import App from './components/App';

const store = createStore(rootReducer);

ReactDOM.render(
   <Provider store={store}>
      <App />
   </Provider>,
   document.getElementById('root')
);

Đây,

  • Tạo một cửa hàng bằng cách sử dụng createStore bằng cách đính kèm bộ giảm tốc của chúng tôi.

  • Đã sử dụng thành phần Nhà cung cấp từ thư viện React redux và đặt cửa hàng làm đạo cụ, cho phép tất cả thành phần lồng nhau kết nối với cửa hàng bằng cách sử dụng kết nối api.

Cuối cùng, tạo một thư mục chung trong thư mục gốc và tạo tệp index.html .

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="utf-8">
      <title>React Containment App</title>
   </head>
   <body>
      <div id="root"></div>
      <script type="text/JavaScript" src="./index.js"></script>
   </body>
</html>

Tiếp theo, phục vụ ứng dụng bằng lệnh npm.

npm start