Redux - Hướng dẫn nhanh

Redux là một vùng chứa trạng thái có thể dự đoán được cho các ứng dụng JavaScript. Khi ứng dụng phát triển, thật khó để giữ cho nó có tổ chức và duy trì luồng dữ liệu. Redux giải quyết vấn đề này bằng cách quản lý trạng thái của ứng dụng với một đối tượng toàn cục duy nhất được gọi là Store. Các nguyên tắc cơ bản của Redux giúp duy trì tính nhất quán trong suốt ứng dụng của bạn, giúp gỡ lỗi và kiểm tra dễ dàng hơn.

Quan trọng hơn, nó cung cấp cho bạn khả năng chỉnh sửa mã trực tiếp kết hợp với trình gỡ lỗi du hành thời gian. Có thể linh hoạt đi với bất kỳ lớp chế độ xem nào như React, Angular, Vue, v.v.

Nguyên tắc của Redux

Khả năng dự đoán của Redux được xác định bởi ba nguyên tắc quan trọng nhất như được đưa ra dưới đây:

Nguồn chân lý duy nhất

Trạng thái của toàn bộ ứng dụng của bạn được lưu trữ trong một cây đối tượng trong một cửa hàng duy nhất. Vì toàn bộ trạng thái ứng dụng được lưu trữ trong một cây duy nhất nên việc gỡ lỗi trở nên dễ dàng và phát triển nhanh hơn.

Trạng thái là Chỉ đọc

Cách duy nhất để thay đổi trạng thái là phát ra một hành động, một đối tượng mô tả những gì đã xảy ra. Điều này có nghĩa là không ai có thể trực tiếp thay đổi trạng thái ứng dụng của bạn.

Các thay đổi được thực hiện với các chức năng thuần túy

Để chỉ định cách cây trạng thái được chuyển đổi bằng các hành động, bạn viết các bộ giảm thuần túy. Bộ giảm tốc là nơi trung tâm diễn ra việc sửa đổi trạng thái. Reducer là một hàm lấy trạng thái và hành động làm đối số và trả về trạng thái mới được cập nhật.

Trước khi cài đặt Redux, we have to install Nodejs and NPM. Dưới đây là các hướng dẫn sẽ giúp bạn cài đặt nó. Bạn có thể bỏ qua các bước này nếu bạn đã cài đặt Nodejs và NPM trong thiết bị của mình.

  • Chuyến thăm https://nodejs.org/ và cài đặt tệp gói.

  • Chạy trình cài đặt, làm theo hướng dẫn và chấp nhận thỏa thuận cấp phép.

  • Khởi động lại thiết bị của bạn để chạy nó.

  • Bạn có thể kiểm tra cài đặt thành công bằng cách mở dấu nhắc lệnh và gõ nút -v. Thao tác này sẽ hiển thị cho bạn phiên bản Node mới nhất trong hệ thống của bạn.

  • Để kiểm tra xem npm đã được cài đặt thành công hay chưa, bạn có thể nhập npm –v để trả về cho bạn phiên bản npm mới nhất.

Để cài đặt redux, bạn có thể làm theo các bước sau:

Chạy lệnh sau trong dấu nhắc lệnh của bạn để cài đặt Redux.

npm install --save redux

Để sử dụng Redux với ứng dụng react, bạn cần cài đặt thêm một phần phụ thuộc như sau:

npm install --save react-redux

Để cài đặt các công cụ dành cho nhà phát triển cho Redux, bạn cần cài đặt phần phụ thuộc sau:

Chạy lệnh dưới đây trong dấu nhắc lệnh của bạn để cài đặt Redux dev-tools.

npm install --save-dev redux-devtools

Nếu bạn không muốn cài đặt các công cụ dành cho nhà phát triển Redux và tích hợp nó vào dự án của mình, bạn có thể cài đặt Redux DevTools Extension cho Chrome và Firefox.

Hãy giả sử trạng thái ứng dụng của chúng ta được mô tả bởi một đối tượng đơn giản có tên là initialState như sau -

const initialState = {
   isLoading: false,
   items: [],
   hasError: false
};

Mọi đoạn mã trong ứng dụng của bạn không thể thay đổi trạng thái này. Để thay đổi trạng thái, bạn cần gửi một hành động.

Hành động là gì?

Một hành động là một đối tượng đơn giản mô tả ý định gây ra thay đổi với một thuộc tính kiểu. Nó phải có thuộc tính type cho biết loại hành động nào đang được thực hiện. Lệnh cho hành động như sau:

return {
   type: 'ITEMS_REQUEST', //action type
   isLoading: true //payload information
}

Các hành động và trạng thái được tổ chức cùng nhau bởi một chức năng gọi là Bộ giảm tốc. Một hành động được thực hiện với ý định gây ra thay đổi. Sự thay đổi này được thực hiện bởi bộ giảm tốc. Giảm tốc là cách duy nhất để thay đổi trạng thái trong Redux, làm cho nó dễ dự đoán, tập trung và dễ gỡ lỗi hơn. Một hàm giảm thiểu xử lý hành động 'ITEMS_REQUEST' như sau:

const reducer = (state = initialState, action) => { //es6 arrow function
   switch (action.type) {
      case 'ITEMS_REQUEST':
         return Object.assign({}, state, {
            isLoading: action.isLoading
         })
      default:
         return state;
   }
}

Redux có một cửa hàng duy nhất chứa trạng thái ứng dụng. Nếu bạn muốn chia nhỏ mã của mình trên cơ sở logic xử lý dữ liệu, bạn nên bắt đầu chia nhỏ các bộ giảm của mình thay vì lưu trữ trong Redux.

Chúng ta sẽ thảo luận về cách chúng ta có thể tách bộ giảm và kết hợp nó với cửa hàng ở phần sau trong hướng dẫn này.

Các thành phần của Redux như sau:

Redux tuân theo luồng dữ liệu một chiều. Nó có nghĩa là dữ liệu ứng dụng của bạn sẽ tuân theo luồng dữ liệu ràng buộc một chiều. Khi ứng dụng phát triển và trở nên phức tạp, thật khó để tái tạo các vấn đề và thêm các tính năng mới nếu bạn không kiểm soát được trạng thái ứng dụng của mình.

Redux giảm độ phức tạp của mã bằng cách thực thi hạn chế về cách thức và thời điểm cập nhật trạng thái có thể xảy ra. Bằng cách này, quản lý các trạng thái cập nhật rất dễ dàng. Chúng ta đã biết về các hạn chế như ba nguyên tắc của Redux. Sơ đồ sau sẽ giúp bạn hiểu rõ hơn về luồng dữ liệu Redux -

  • Một hành động được thực hiện khi người dùng tương tác với ứng dụng.

  • Chức năng giảm thiểu gốc được gọi với trạng thái hiện tại và hành động được điều động. Bộ giảm tốc gốc có thể phân chia nhiệm vụ cho các hàm bộ giảm nhỏ hơn, cuối cùng sẽ trả về một trạng thái mới.

  • Cửa hàng thông báo chế độ xem bằng cách thực hiện các chức năng gọi lại của họ.

  • Chế độ xem có thể truy xuất trạng thái đã cập nhật và kết xuất lại.

Cửa hàng là một cây đối tượng bất biến trong Redux. Lưu trữ là một vùng chứa trạng thái chứa trạng thái của ứng dụng. Redux chỉ có thể có một cửa hàng duy nhất trong ứng dụng của bạn. Bất cứ khi nào một cửa hàng được tạo trong Redux, bạn cần chỉ định trình giảm bớt.

Hãy để chúng tôi xem cách chúng tôi có thể tạo một cửa hàng bằng cách sử dụng createStorephương thức từ Redux. Một người cần nhập gói createStore từ thư viện Redux hỗ trợ quá trình tạo cửa hàng như hình dưới đây:

import { createStore } from 'redux';
import reducer from './reducers/reducer'
const store = createStore(reducer);

Một hàm createStore có thể có ba đối số. Sau đây là cú pháp:

createStore(reducer, [preloadedState], [enhancer])

Bộ giảm tốc là một hàm trả về trạng thái tiếp theo của ứng dụng. PreloadedState là một đối số tùy chọn và là trạng thái ban đầu của ứng dụng của bạn. Một chất tăng cường cũng là một đối số tùy chọn. Nó sẽ giúp bạn nâng cao cửa hàng với khả năng của bên thứ ba.

Một cửa hàng có ba phương pháp quan trọng như dưới đây:

getState

Nó giúp bạn truy xuất trạng thái hiện tại của cửa hàng Redux của bạn.

Cú pháp cho getState như sau:

store.getState()

cử đi

Nó cho phép bạn gửi một hành động để thay đổi trạng thái trong ứng dụng của bạn.

Cú pháp của công văn như sau:

store.dispatch({type:'ITEMS_REQUEST'})

đăng ký

Nó giúp bạn đăng ký một cuộc gọi lại mà cửa hàng Redux sẽ gọi khi một hành động đã được gửi đi. Ngay sau khi trạng thái Redux được cập nhật, chế độ xem sẽ tự động hiển thị lại.

Cú pháp của công văn như sau:

store.subscribe(()=>{ console.log(store.getState());})

Lưu ý rằng hàm đăng ký trả về một hàm để hủy đăng ký người nghe. Để hủy đăng ký người nghe, chúng ta có thể sử dụng đoạn mã dưới đây:

const unsubscribe = store.subscribe(()=>{console.log(store.getState());});
unsubscribe();

Các hành động là nguồn thông tin duy nhất cho cửa hàng theo tài liệu chính thức của Redux. Nó mang một lượng lớn thông tin từ ứng dụng của bạn để lưu trữ.

Như đã thảo luận trước đó, các hành động là đối tượng JavaScript thuần túy phải có thuộc tính type để chỉ ra loại hành động được thực hiện. Nó cho chúng ta biết những gì đã xảy ra. Các loại phải được định nghĩa là hằng số chuỗi trong ứng dụng của bạn như được cung cấp bên dưới:

const ITEMS_REQUEST = 'ITEMS_REQUEST';

Ngoài thuộc tính type này, cấu trúc của một đối tượng action hoàn toàn phụ thuộc vào nhà phát triển. Bạn nên giữ đối tượng hành động của mình càng nhẹ càng tốt và chỉ chuyển những thông tin cần thiết.

Để thực hiện bất kỳ thay đổi nào trong cửa hàng, trước tiên bạn cần thực hiện một hành động bằng cách sử dụng hàm store.dispatch (). Đối tượng hành động như sau:

{ type: GET_ORDER_STATUS , payload: {orderId,userId } }
{ type: GET_WISHLIST_ITEMS, payload: userId }

Người tạo Hành động

Người tạo hành động là các chức năng gói gọn quá trình tạo một đối tượng hành động. Các hàm này chỉ đơn giản trả về một đối tượng Js thuần túy là một hành động. Nó thúc đẩy việc viết mã sạch và giúp đạt được khả năng tái sử dụng.

Hãy để chúng tôi tìm hiểu về trình tạo hành động cho phép bạn gửi một hành động, ‘ITEMS_REQUEST’yêu cầu dữ liệu danh sách mặt hàng sản phẩm từ máy chủ. Trong khi đó,isLoading trạng thái được thực hiện đúng trong loại hành động 'ITEMS_REQUEST' để chỉ ra rằng các mục đang tải và dữ liệu vẫn không được nhận từ máy chủ.

Ban đầu, trạng thái isLoading là sai trong initialStateđối tượng giả định không có gì đang tải. Khi dữ liệu được nhận tại trình duyệt, trạng thái isLoading sẽ được trả về là false trong loại hành động 'ITEMS_REQUEST_SUCCESS' trong trình thu gọn tương ứng. Trạng thái này có thể được sử dụng như một chỗ dựa trong các thành phần phản ứng để hiển thị trình tải / thông báo trên trang của bạn trong khi yêu cầu dữ liệu được bật. Người tạo hành động như sau:

const ITEMS_REQUEST = ‘ITEMS_REQUEST’ ;
const ITEMS_REQUEST_SUCCESS = ‘ITEMS_REQUEST_SUCCESS’ ;
export function itemsRequest(bool,startIndex,endIndex) {
   let payload = {
      isLoading: bool,
      startIndex,
      endIndex
   }
   return {
      type: ITEMS_REQUEST,
      payload
   }
}
export function itemsRequestSuccess(bool) {
   return {
      type: ITEMS_REQUEST_SUCCESS,
      isLoading: bool,
   }
}

Để gọi một hàm điều phối, bạn cần chuyển hành động làm đối số cho hàm điều phối.

dispatch(itemsRequest(true,1, 20));
dispatch(itemsRequestSuccess(false));

Bạn có thể gửi một hành động bằng cách sử dụng trực tiếp store.dispatch (). Tuy nhiên, có nhiều khả năng bạn truy cập nó bằng phương thức trình trợ giúp react-Redux được gọi làconnect(). Bạn cũng có thể dùngbindActionCreators() phương pháp để ràng buộc nhiều người tạo hành động với chức năng điều phối.

Một hàm là một quá trình nhận đầu vào được gọi là đối số và tạo ra một số đầu ra được gọi là giá trị trả về. Một hàm được gọi là thuần túy nếu nó tuân theo các quy tắc sau:

  • Một hàm trả về cùng một kết quả cho các đối số giống nhau.

  • Đánh giá của nó không có tác dụng phụ, tức là nó không làm thay đổi dữ liệu đầu vào.

  • Không có đột biến của các biến cục bộ và toàn cục.

  • Nó không phụ thuộc vào trạng thái bên ngoài như một biến toàn cục.

Chúng ta hãy lấy ví dụ về một hàm trả về hai lần giá trị được truyền làm đầu vào cho hàm. Nói chung, nó được viết là, f (x) => x * 2. Nếu một hàm được gọi với giá trị đối số 2, thì đầu ra sẽ là 4, f (2) => 4.

Hãy để chúng tôi viết định nghĩa của hàm trong JavaScript như hình dưới đây:

const double = x => x*2; // es6 arrow function
console.log(double(2));  // 4

Here, double is a pure function.

Theo ba nguyên tắc trong Redux, các thay đổi phải được thực hiện bởi một hàm thuần túy, tức là bộ giảm trong Redux. Bây giờ, một câu hỏi được đặt ra là tại sao một bộ giảm tốc phải là một hàm thuần túy.

Giả sử, bạn muốn gửi một hành động có kiểu là 'ADD_TO_CART_SUCCESS' để thêm một mặt hàng vào ứng dụng giỏ hàng của bạn bằng cách nhấp vào nút thêm vào giỏ hàng.

Hãy để chúng tôi giả sử bộ giảm giá đang thêm một mặt hàng vào giỏ hàng của bạn như được đưa ra bên dưới -

const initialState = {
   isAddedToCart: false;
}
const addToCartReducer = (state = initialState, action) => { //es6 arrow function
   switch (action.type) {
      case 'ADD_TO_CART_SUCCESS' :
         state.isAddedToCart = !state.isAddedToCart; //original object altered
         return state;
      default:
         return state;
   }
}
export default addToCartReducer ;

Hãy giả sử, isAddedToCart là thuộc tính trên đối tượng trạng thái cho phép bạn quyết định thời điểm tắt nút 'thêm vào giỏ hàng' cho mặt hàng bằng cách trả về giá trị Boolean ‘true or false’. Điều này ngăn người dùng thêm cùng một sản phẩm nhiều lần. Bây giờ, thay vì trả về một đối tượng mới, chúng ta đang thay đổi prop isAddedToCart trên trạng thái như trên. Bây giờ nếu chúng tôi cố gắng thêm một mặt hàng vào giỏ hàng, không có gì xảy ra. Nút Thêm vào giỏ hàng sẽ không bị tắt.

Lý do cho hành vi này như sau:

Redux so sánh các đối tượng cũ và mới theo vị trí bộ nhớ của cả hai đối tượng. Nó mong đợi một đối tượng mới từ bộ giảm tốc nếu bất kỳ thay đổi nào đã xảy ra. Và nó cũng mong đợi để lấy lại đối tượng cũ nếu không có thay đổi nào xảy ra. Trong trường hợp này, nó là như nhau. Vì lý do này, Redux giả định rằng không có gì xảy ra.

Vì vậy, điều cần thiết cho một bộ giảm là một hàm thuần túy trong Redux. Sau đây là cách viết nó không bị đột biến -

const initialState = {
   isAddedToCart: false;
}
const addToCartReducer = (state = initialState, action) => { //es6 arrow function
   switch (action.type) {
      case 'ADD_TO_CART_SUCCESS' :
         return {
            ...state,
            isAddedToCart: !state.isAddedToCart
         }
      default:
         return state;
   }
}
export default addToCartReducer;

Giảm thiểu là một chức năng thuần túy trong Redux. Các chức năng thuần túy có thể dự đoán được. Bộ giảm thiểu là cách duy nhất để thay đổi trạng thái trong Redux. Nó là nơi duy nhất mà bạn có thể viết logic và tính toán. Hàm Reducer sẽ chấp nhận trạng thái trước đó của ứng dụng và hành động đang được gửi đi, tính toán trạng thái tiếp theo và trả về đối tượng mới.

Một số điều sau không bao giờ được thực hiện bên trong bộ giảm tốc:

  • Sự thay đổi các đối số của hàm
  • Lệnh gọi API và logic định tuyến
  • Gọi hàm không thuần túy, ví dụ Math.random ()

Sau đây là cú pháp của một bộ rút gọn:

(state,action) => newState

Hãy để chúng tôi tiếp tục ví dụ về hiển thị danh sách các mặt hàng sản phẩm trên trang web, được thảo luận trong mô-đun người tạo hành động. Hãy để chúng tôi xem bên dưới làm thế nào để viết giảm thiểu của nó.

const initialState = {
   isLoading: false,
   items: []
};
const reducer = (state = initialState, action) => {
   switch (action.type) {
      case 'ITEMS_REQUEST':
         return Object.assign({}, state, {
            isLoading: action.payload.isLoading
         })
      case ‘ITEMS_REQUEST_SUCCESS':
         return Object.assign({}, state, {
            items: state.items.concat(action.items),
            isLoading: action.isLoading
         })
      default:
         return state;
   }
}
export default reducer;

Thứ nhất, nếu bạn không đặt trạng thái thành 'InitialState', Redux sẽ gọi trình giảm thiểu với trạng thái không xác định. Trong ví dụ mã này, hàm concat () của JavaScript được sử dụng trong 'ITEMS_REQUEST_SUCCESS', hàm này không thay đổi mảng hiện có; thay vào đó trả về một mảng mới.

Bằng cách này, bạn có thể tránh được sự đột biến của trạng thái. Không bao giờ viết trực tiếp cho tiểu bang. Trong 'ITEMS_REQUEST', chúng tôi phải đặt giá trị trạng thái từ hành động nhận được.

Người ta đã thảo luận rằng chúng ta có thể viết logic của mình trong bộ giảm thiểu và có thể phân chia nó trên cơ sở dữ liệu logic. Hãy để chúng tôi xem cách chúng tôi có thể tách các bộ giảm thiểu và kết hợp chúng với nhau làm bộ giảm tốc gốc khi xử lý một ứng dụng lớn.

Giả sử, chúng tôi muốn thiết kế một trang web nơi người dùng có thể truy cập trạng thái đơn đặt hàng sản phẩm và xem thông tin danh sách yêu thích. Chúng tôi có thể phân tách logic trong các tệp giảm bớt khác nhau và làm cho chúng hoạt động độc lập. Chúng tôi giả định rằng hành động GET_ORDER_STATUS được thực hiện để nhận trạng thái của đơn đặt hàng tương ứng với một số id đơn đặt hàng và id người dùng.

/reducer/orderStatusReducer.js
import { GET_ORDER_STATUS } from ‘../constants/appConstant’;
export default function (state = {} , action) {
   switch(action.type) {
      case GET_ORDER_STATUS:
         return { ...state, orderStatusData: action.payload.orderStatus };
      default:
         return state;
   }
}

Tương tự, giả sử hành động GET_WISHLIST_ITEMS được thực hiện để lấy thông tin danh sách yêu thích của người dùng tương ứng với người dùng.

/reducer/getWishlistDataReducer.js
import { GET_WISHLIST_ITEMS } from ‘../constants/appConstant’;
export default function (state = {}, action) {
   switch(action.type) {
      case GET_WISHLIST_ITEMS:
         return { ...state, wishlistData: action.payload.wishlistData };
      default:
         return state;
   }
}

Bây giờ, chúng ta có thể kết hợp cả hai bộ giảm bằng cách sử dụng tiện ích Redux connectReducers. Các hàm kết hợp tạo ra một hàm trả về một đối tượng có giá trị là các hàm rút gọn khác nhau. Bạn có thể nhập tất cả các bộ giảm trong tệp giảm chỉ mục và kết hợp chúng với nhau thành một đối tượng với tên tương ứng của chúng.

/reducer/index.js
import { combineReducers } from ‘redux’;
import OrderStatusReducer from ‘./orderStatusReducer’;
import GetWishlistDataReducer from ‘./getWishlistDataReducer’;

const rootReducer = combineReducers ({
   orderStatusReducer: OrderStatusReducer,
   getWishlistDataReducer: GetWishlistDataReducer
});
export default rootReducer;

Bây giờ, bạn có thể chuyển rootReducer này vào phương thức createStore như sau:

const store = createStore(rootReducer);

Bản thân Redux là đồng bộ, vì vậy async hoạt động như network requestlàm việc với Redux? Đây là phần mềm trung gian có ích. Như đã thảo luận trước đó, bộ giảm là nơi ghi tất cả logic thực thi. Reducer không liên quan đến việc ai thực hiện nó, mất bao nhiêu thời gian hoặc ghi trạng thái của ứng dụng trước và sau khi hành động được thực hiện.

Trong trường hợp này, chức năng phần mềm trung gian của Redux cung cấp một phương tiện để tương tác với hành động được gửi đi trước khi chúng tiếp cận trình giảm thiểu. Các chức năng phần mềm trung gian được tùy chỉnh có thể được tạo ra bằng cách viết các hàm bậc cao (một hàm trả về một hàm khác), nó bao quanh một số logic. Nhiều phần mềm trung gian có thể được kết hợp với nhau để thêm chức năng mới và mỗi phần mềm trung gian không yêu cầu kiến ​​thức về những gì xảy ra trước và sau. Bạn có thể tưởng tượng phần mềm trung gian ở đâu đó giữa hành động được gửi đi và trình giảm bớt.

Thông thường, phần mềm trung gian được sử dụng để xử lý các hành động không đồng bộ trong ứng dụng của bạn. Redux cung cấp API được gọi là applyMiddleware cho phép chúng ta sử dụng phần mềm trung gian tùy chỉnh cũng như phần mềm trung gian Redux như redux-thunk và redux-promise. Nó áp dụng phần mềm trung gian để lưu trữ. Cú pháp của việc sử dụng API applyMiddleware là:

applyMiddleware(...middleware)

Và điều này có thể được áp dụng để lưu trữ như sau:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(rootReducer, applyMiddleware(thunk));

Phần mềm trung gian sẽ cho phép bạn viết một trình điều phối hành động trả về một hàm thay vì một đối tượng hành động. Ví dụ cho điều tương tự được hiển thị bên dưới -

function getUser() {
   return function() {
      return axios.get('/get_user_details');
   };
}

Công văn có điều kiện có thể được viết bên trong phần mềm trung gian. Mỗi phần mềm trung gian nhận được công văn của cửa hàng để chúng có thể gửi hành động mới và các hàm getState làm đối số để chúng có thể truy cập trạng thái hiện tại và trả về một hàm. Bất kỳ giá trị trả về nào từ một hàm bên trong sẽ có sẵn dưới dạng giá trị của chính hàm điều phối.

Sau đây là cú pháp của phần mềm trung gian:

({ getState, dispatch }) => next => action

Hàm getState hữu ích để quyết định xem dữ liệu mới sẽ được tìm nạp hay kết quả bộ nhớ cache sẽ được trả về, tùy thuộc vào trạng thái hiện tại.

Hãy để chúng tôi xem một ví dụ về chức năng trình ghi phần mềm trung gian tùy chỉnh. Nó chỉ ghi nhật ký hành động và trạng thái mới.

import { createStore, applyMiddleware } from 'redux'
import userLogin from './reducers'

function logger({ getState }) {
   return next => action => {
      console.log(‘action’, action);
      const returnVal = next(action);
      console.log('state when action is dispatched', getState()); 
      return returnVal;
   }
}

Bây giờ áp dụng phần mềm trung gian của trình ghi nhật ký cho cửa hàng bằng cách viết dòng mã sau:

const store = createStore(userLogin , initialState=[ ] , applyMiddleware(logger));

Gửi một hành động để kiểm tra hành động đã gửi và trạng thái mới bằng cách sử dụng mã bên dưới -

store.dispatch({
   type: 'ITEMS_REQUEST', 
	isLoading: true
})

Một ví dụ khác về phần mềm trung gian nơi bạn có thể xử lý khi nào hiển thị hoặc ẩn bộ nạp được đưa ra bên dưới. Phần mềm trung gian này hiển thị trình tải khi bạn đang yêu cầu bất kỳ tài nguyên nào và ẩn nó khi yêu cầu tài nguyên đã được hoàn thành.

import isPromise from 'is-promise';

function loaderHandler({ dispatch }) {
   return next => action => {
      if (isPromise(action)) {
         dispatch({ type: 'SHOW_LOADER' });
         action
            .then(() => dispatch({ type: 'HIDE_LOADER' }))
            .catch(() => dispatch({ type: 'HIDE_LOADER' }));
      }
      return next(action);
   };
}
const store = createStore(
   userLogin , initialState = [ ] , 
   applyMiddleware(loaderHandler)
);

Redux-Devtools cung cấp cho chúng tôi nền tảng gỡ lỗi cho các ứng dụng Redux. Nó cho phép chúng tôi thực hiện gỡ lỗi du hành thời gian và chỉnh sửa trực tiếp. Một số tính năng trong tài liệu chính thức như sau:

  • Nó cho phép bạn kiểm tra mọi trạng thái và tải trọng hành động.

  • Nó cho phép bạn quay ngược thời gian bằng các hành động “hủy bỏ”.

  • Nếu bạn thay đổi mã giảm thiểu, mỗi hành động "theo giai đoạn" sẽ được đánh giá lại.

  • Nếu bộ giảm tốc ném, chúng tôi có thể xác định lỗi và hành động này đã xảy ra.

  • Với công cụ nâng cao lưu trữ Kiên trì (), bạn có thể liên tục gỡ lỗi các phiên qua các lần tải lại trang.

Có hai biến thể của Redux dev-tools như được đưa ra bên dưới:

Redux DevTools - Nó có thể được cài đặt dưới dạng một gói và được tích hợp vào ứng dụng của bạn như được cung cấp bên dưới -

https://github.com/reduxjs/redux-devtools/blob/master/docs/Walkthrough.md#manual-integration

Redux DevTools Extension - Tiện ích mở rộng trình duyệt triển khai các công cụ dành cho nhà phát triển tương tự cho Redux như sau:

https://github.com/zalmoxisus/redux-devtools-extension

Bây giờ chúng ta hãy kiểm tra cách chúng ta có thể bỏ qua các hành động và quay ngược thời gian với sự trợ giúp của công cụ nhà phát triển Redux. Ảnh chụp màn hình sau giải thích về các hành động chúng tôi đã thực hiện trước đó để có được danh sách các mặt hàng. Tại đây, chúng ta có thể thấy các hành động được gửi trong tab trình kiểm tra. Ở bên phải, bạn có thể thấy tab Demo cho bạn thấy sự khác biệt trong cây trạng thái.

Bạn sẽ quen với công cụ này khi bắt đầu sử dụng. Bạn có thể gửi một hành động mà không cần viết mã thực chỉ từ công cụ plugin Redux này. Tùy chọn Dispatcher ở hàng cuối cùng sẽ giúp bạn điều này. Hãy để chúng tôi kiểm tra hành động cuối cùng mà các mục được tìm nạp thành công.

Chúng tôi đã nhận được một mảng các đối tượng dưới dạng phản hồi từ máy chủ. Tất cả dữ liệu có sẵn để hiển thị danh sách trên trang của chúng tôi. Bạn cũng có thể theo dõi trạng thái của cửa hàng cùng lúc bằng cách nhấp vào tab trạng thái ở phía trên bên phải.

Trong các phần trước, chúng ta đã tìm hiểu về gỡ lỗi du hành thời gian. Bây giờ chúng ta hãy kiểm tra cách bỏ qua một hành động và quay ngược thời gian để phân tích trạng thái ứng dụng của chúng ta. Khi bạn nhấp vào bất kỳ loại hành động nào, hai tùy chọn: 'Nhảy' và 'Bỏ qua' sẽ xuất hiện.

Bằng cách nhấp vào nút bỏ qua trên một loại hành động nhất định, bạn có thể bỏ qua hành động cụ thể. Nó hoạt động như thể hành động chưa bao giờ xảy ra. Khi bạn nhấp vào nút nhảy trên loại hành động nhất định, nó sẽ đưa bạn đến trạng thái khi hành động đó xảy ra và bỏ qua tất cả các hành động còn lại theo trình tự. Bằng cách này, bạn sẽ có thể duy trì trạng thái khi một hành động cụ thể xảy ra. Tính năng này rất hữu ích trong việc gỡ lỗi và tìm lỗi trong ứng dụng.

Chúng tôi đã bỏ qua hành động cuối cùng và tất cả dữ liệu danh sách từ nền đã biến mất. Nó quay trở lại thời điểm khi dữ liệu của các mục chưa đến và ứng dụng của chúng tôi không có dữ liệu để hiển thị trên trang. Nó thực sự làm cho việc viết mã dễ dàng và gỡ lỗi dễ dàng hơn.

Kiểm tra mã Redux rất dễ dàng vì chúng tôi chủ yếu viết các hàm và hầu hết chúng đều thuần túy. Vì vậy, chúng tôi có thể kiểm tra nó mà không cần chế nhạo họ. Ở đây, chúng tôi đang sử dụng JEST làm công cụ thử nghiệm. Nó hoạt động trong môi trường nút và không truy cập DOM.

Chúng tôi có thể cài đặt JEST với mã được cung cấp bên dưới -

npm install --save-dev jest

Với babel, bạn cần cài đặt babel-jest như sau -

npm install --save-dev babel-jest

Và định cấu hình nó để sử dụng các tính năng babel-preset-env trong tệp .babelrc như sau:

{ 
   "presets": ["@babel/preset-env"] 
}
And add the following script in your package.json:
{ 
   //Some other code 
   "scripts": {
      //code
      "test": "jest", 
      "test:watch": "npm test -- --watch" 
   }, 
   //code 
}

Cuối cùng, run npm test or npm run test. Hãy để chúng tôi kiểm tra cách chúng tôi có thể viết các trường hợp thử nghiệm cho người tạo và trình giảm bớt hành động.

Các trường hợp thử nghiệm dành cho người sáng tạo hành động

Giả sử bạn có người tạo hành động như hình dưới đây -

export function itemsRequestSuccess(bool) {
   return {
      type: ITEMS_REQUEST_SUCCESS,
      isLoading: bool,
   }
}

Người tạo hành động này có thể được kiểm tra như được cung cấp bên dưới:

import * as action from '../actions/actions';
import * as types from '../../constants/ActionTypes';

describe('actions', () => {
   it('should create an action to check if item is loading', () => { 
      const isLoading = true, 
      const expectedAction = { 
         type: types.ITEMS_REQUEST_SUCCESS, isLoading 
      } 
      expect(actions.itemsRequestSuccess(isLoading)).toEqual(expectedAction) 
   })
})

Các trường hợp kiểm tra cho bộ giảm tốc

Chúng tôi đã biết rằng bộ giảm tốc sẽ trả về trạng thái mới khi hành động được áp dụng. Vì vậy, bộ giảm tốc được thử nghiệm trên hành vi này.

Hãy xem xét một bộ giảm tốc như được đưa ra bên dưới:

const initialState = {
   isLoading: false
};
const reducer = (state = initialState, action) => {
   switch (action.type) {
      case 'ITEMS_REQUEST':
         return Object.assign({}, state, {
            isLoading: action.payload.isLoading
         })
      default:
         return state;
   }
}
export default reducer;

Để kiểm tra bộ giảm tốc ở trên, chúng ta cần chuyển trạng thái và hành động cho bộ giảm tốc và trả về trạng thái mới như hình dưới đây:

import reducer from '../../reducer/reducer' 
import * as types from '../../constants/ActionTypes'

describe('reducer initial state', () => {
   it('should return the initial state', () => {
      expect(reducer(undefined, {})).toEqual([
         {
            isLoading: false,
         }
      ])
   })
   it('should handle ITEMS_REQUEST', () => {
      expect(
         reducer(
            {
               isLoading: false,
            },
            {
               type: types.ITEMS_REQUEST,
               payload: { isLoading: true }
            }
         )
      ).toEqual({
         isLoading: true
      })
   })
})

Nếu bạn không quen với việc viết test case, bạn có thể kiểm tra những điều cơ bản về JEST .

Trong các chương trước, chúng ta đã tìm hiểu Redux là gì và nó hoạt động như thế nào. Bây giờ chúng ta hãy kiểm tra sự tích hợp của phần xem với Redux. Bạn có thể thêm bất kỳ lớp xem nào vào Redux. Chúng ta cũng sẽ thảo luận về thư viện phản ứng và Redux.

Hãy cho chúng tôi biết nếu các thành phần phản ứng khác nhau cần hiển thị cùng một dữ liệu theo những cách khác nhau mà không cần chuyển nó làm chỗ dựa cho tất cả các thành phần từ thành phần cấp cao nhất đến cấp dưới. Nó sẽ là lý tưởng để lưu trữ nó bên ngoài các thành phần phản ứng. Bởi vì nó giúp truy xuất dữ liệu nhanh hơn vì bạn không cần phải chuyển dữ liệu xuống các thành phần khác nhau.

Hãy để chúng tôi thảo luận về cách khả thi với Redux. Redux cung cấp gói react-redux để liên kết các thành phần phản ứng với hai tiện ích như dưới đây:

  • Provider
  • Connect

Nhà cung cấp cung cấp cho cửa hàng phần còn lại của ứng dụng. Chức năng kết nối giúp thành phần phản ứng kết nối với cửa hàng, phản ứng với mỗi thay đổi xảy ra trong trạng thái của cửa hàng.

Hãy để chúng tôi xem xét root index.js tệp tạo cửa hàng và sử dụng trình cung cấp cho phép lưu trữ phần còn lại của ứng dụng trong ứng dụng react-redux.

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers/reducer'
import thunk from 'redux-thunk';
import App from './components/app'
import './index.css';

const store = createStore(
   reducer,
   window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
   applyMiddleware(thunk)
)
render(
   <Provider store = {store}>
      <App />
   </Provider>,
   document.getElementById('root')
)

Bất cứ khi nào thay đổi xảy ra trong ứng dụng react-redux, mapStateToProps () sẽ được gọi. Trong hàm này, chúng tôi chỉ định chính xác trạng thái mà chúng tôi cần cung cấp cho thành phần phản ứng của mình.

Với sự trợ giúp của hàm connect () được giải thích bên dưới, chúng tôi đang kết nối trạng thái của ứng dụng này với thành phần phản ứng. Connect () là một hàm bậc cao lấy thành phần làm tham số. Nó thực hiện các hoạt động nhất định và trả về một thành phần mới với dữ liệu chính xác mà cuối cùng chúng tôi đã xuất.

Với sự trợ giúp của mapStateToProps (), chúng tôi cung cấp các trạng thái lưu trữ này làm chỗ dựa cho thành phần phản ứng của chúng tôi. Mã này có thể được bao bọc trong một thành phần vùng chứa. Động cơ là tách biệt các mối quan tâm như tìm nạp dữ liệu, mối quan tâm hiển thị và khả năng sử dụng lại.

import { connect } from 'react-redux'
import Listing from '../components/listing/Listing' //react component
import makeApiCall from '../services/services' //component to make api call

const mapStateToProps = (state) => {
   return {
      items: state.items,
      isLoading: state.isLoading
   };
};
const mapDispatchToProps = (dispatch) => {
   return {
      fetchData: () => dispatch(makeApiCall())
   };
};
export default connect(mapStateToProps, mapDispatchToProps)(Listing);

Định nghĩa của một thành phần để thực hiện một cuộc gọi api trong tệp services.js như sau:

import axios from 'axios'
import { itemsLoading, itemsFetchDataSuccess } from '../actions/actions'

export default function makeApiCall() {
   return (dispatch) => {
      dispatch(itemsLoading(true));
      axios.get('http://api.tvmaze.com/shows')
      .then((response) => {
         if (response.status !== 200) {
            throw Error(response.statusText);
         }
         dispatch(itemsLoading(false));
         return response;
      })
      .then((response) => dispatch(itemsFetchDataSuccess(response.data)))
   };
}

Hàm mapDispatchToProps () nhận hàm điều phối như một tham số và trả về cho bạn các đạo cụ gọi lại dưới dạng đối tượng đơn giản mà bạn chuyển cho thành phần phản ứng của mình.

Tại đây, bạn có thể truy cập fetchData như một phần hỗ trợ trong thành phần danh sách phản ứng của bạn, thành phần này sẽ gửi một hành động để thực hiện một lệnh gọi API. mapDispatchToProps () được sử dụng để gửi một hành động để lưu trữ. Trong react-redux, các thành phần không thể truy cập trực tiếp vào cửa hàng. Cách duy nhất là sử dụng connect ().

Hãy để chúng tôi hiểu cách hoạt động của react-redux thông qua sơ đồ dưới đây -

STORE - Lưu trữ tất cả trạng thái ứng dụng của bạn dưới dạng đối tượng JavaScript

PROVIDER - Làm cho các cửa hàng có sẵn

CONTAINER - Nhận trạng thái ứng dụng và cung cấp nó làm chỗ dựa cho các thành phần

COMPONENT - Người dùng tương tác thông qua thành phần xem

ACTIONS - Gây ra thay đổi trong cửa hàng, nó có thể thay đổi hoặc không thể thay đổi trạng thái ứng dụng của bạn

REDUCER - Cách duy nhất để thay đổi trạng thái ứng dụng, chấp nhận trạng thái và hành động và trả về trạng thái đã cập nhật.

Tuy nhiên, Redux là một thư viện độc lập và có thể được sử dụng với bất kỳ lớp giao diện người dùng nào. React-redux là Redux chính thức, liên kết giao diện người dùng với phản ứng. Hơn nữa, nó khuyến khích một cấu trúc ứng dụng Redux phản ứng tốt. React-redux thực hiện tối ưu hóa hiệu suất bên trong, để kết xuất thành phần chỉ xảy ra khi cần thiết.

Tóm lại, Redux không được thiết kế để viết mã ngắn nhất và nhanh nhất. Nó nhằm cung cấp một vùng chứa quản lý trạng thái có thể dự đoán được. Nó giúp chúng tôi hiểu khi nào một trạng thái nhất định thay đổi hoặc dữ liệu đến từ đâu.

Đây là một ví dụ nhỏ về ứng dụng react và Redux. Bạn cũng có thể thử phát triển các ứng dụng nhỏ. Mã mẫu cho bộ đếm tăng hoặc giảm được đưa ra dưới đây:

Đây là tệp gốc chịu trách nhiệm tạo cửa hàng và hiển thị thành phần ứng dụng phản ứng của chúng tôi.

/src/index.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux';
import reducer from '../src/reducer/index'
import App from '../src/App'
import './index.css';

const store = createStore(
   reducer,
   window.__REDUX_DEVTOOLS_EXTENSION__ && 
   window.__REDUX_DEVTOOLS_EXTENSION__()
)
render(
   <Provider store = {store}>
      <App />
   </Provider>, document.getElementById('root')
)

Đây là thành phần gốc của phản ứng. Nó chịu trách nhiệm hiển thị thành phần bộ chứa bộ đếm khi còn nhỏ.

/src/app.js

import React, { Component } from 'react';
import './App.css';
import Counter from '../src/container/appContainer';

class App extends Component {
   render() {
      return (
         <div className = "App">
            <header className = "App-header">
               <Counter/>
            </header>
         </div>
      );
   }
}
export default App;

Sau đây là thành phần vùng chứa chịu trách nhiệm cung cấp trạng thái của Redux cho thành phần phản ứng:

/container/counterContainer.js

import { connect } from 'react-redux'
import Counter from '../component/counter'
import { increment, decrement, reset } from '../actions';

const mapStateToProps = (state) => {
   return {
      counter: state
   };
};
const mapDispatchToProps = (dispatch) => {
   return {
      increment: () => dispatch(increment()),
      decrement: () => dispatch(decrement()),
      reset: () => dispatch(reset())
   };
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

Dưới đây là thành phần phản ứng chịu trách nhiệm cho phần xem -

/component/counter.js
import React, { Component } from 'react';
class Counter extends Component {
   render() {
      const {counter,increment,decrement,reset} = this.props;
      return (
         <div className = "App">
            <div>{counter}</div>
            <div>
               <button onClick = {increment}>INCREMENT BY 1</button>
            </div>
            <div>
               <button onClick = {decrement}>DECREMENT BY 1</button>
            </div>
            <button onClick = {reset}>RESET</button>
         </div>
      );
   }
}
export default Counter;

Sau đây là những người tạo hành động chịu trách nhiệm tạo một hành động -

/actions/index.js
export function increment() {
   return {
      type: 'INCREMENT'
   }
}
export function decrement() {
   return {
      type: 'DECREMENT'
   }
}
export function reset() {
   return { type: 'RESET' }
}

Dưới đây, chúng tôi đã hiển thị dòng mã cho tệp giảm thiểu chịu trách nhiệm cập nhật trạng thái trong Redux.

reducer/index.js
const reducer = (state = 0, action) => {
   switch (action.type) {
      case 'INCREMENT': return state + 1
      case 'DECREMENT': return state - 1
      case 'RESET' : return 0 default: return state
   }
}
export default reducer;

Ban đầu, ứng dụng trông như sau:

Khi tôi nhấp vào số tăng hai lần, màn hình đầu ra sẽ như hình dưới đây -

Khi chúng tôi giảm nó một lần, nó sẽ hiển thị màn hình sau:

Và thiết lập lại sẽ đưa ứng dụng trở lại trạng thái ban đầu là giá trị bộ đếm 0. Điều này được hiển thị bên dưới -

Hãy để chúng tôi hiểu điều gì xảy ra với các công cụ dành cho nhà phát triển Redux khi hành động gia tăng đầu tiên diễn ra -

Trạng thái của ứng dụng sẽ được chuyển đến thời điểm chỉ hành động tăng dần được gửi đi và phần còn lại của các hành động bị bỏ qua.

Chúng tôi khuyến khích bạn tự mình phát triển một Ứng dụng Todo nhỏ như một nhiệm vụ và hiểu rõ hơn về công cụ Redux.