+3

Sử dụng Redux để quản lý state trong ứng dụng React

Giới thiệu

Ở bài viết trước, mình đã giới thiệu cho các bạn về thành phần và follow của Redux để quản lý state cho ứng dụng Javascript. Link bài viết (Làm quen với Redux để quản lý state cho ứng dụng Javascript) mọi người có thể xem lại để hiểu thêm về Redux nhé. Và trong bài viết này, mình sẽ sử dụng Redux để áp dụng vào ứng dụng React (một Javascript framework cho Front-end rất phổ biết hiện nay).

Thực hiện cài đặt

Trong vài viết này, mình sẽ sử dụng React để thực hiện tạo một ứng dụng đếm (counter) sử dụng kết hợp các Redux, Redux Toolkit và React-Redux.

1. Redux core

Chắc chắn rồi, để bắt đầu thì trước tiên các bạn cần cài đặt Redux

# sử dụng npm
npm install redux --save

# sử dụng yarn
yarn add redux --save

2. React Tokit

Redux-Toolkit được khuyết khích để viết Redux logic, giúp tránh các lỗi thường gặp và làm đơn giản hóa việc viết ứng dụng Redux

# sử dụng npm
npm install @reduxjs/toolkit --save

# sử dụng yarn
yarn add @reduxjs/toolkit --save

3. React-Redux

React-Redux là package giúp cho React Components của bạn có thể tương tác với Redux store bằng cách đọc state và dispatch actions để cập nhật state trong store.

# sử dụng npm
npm install react-redux --save

# sử dụng yarn:
yarn add react-redux --save

4. Sử dụng Create React App

Đây là cách được khuyến khích để bắt đầu một app React Redux bằng mẫu có sẵn kết hợp Redux + Redux Toolkit bằng câu lệnh Create React App

npx create-react-app my-app --template redux

Trong bài viết này mình khuyết khích nếu các bạn mới làm quen với React Redux, thì nên tự set up từ đầu để hiểu rỏ hơn cấu trúc của ứng dụng React Redux nhé.

Các bước thực hiện

1. Tạo Redux store

import { configureStore } from "@reduxjs/toolkit";
import CounterReducer from "../Features/Counter/CounterSlice";

export default configureStore({
  reducer: {
    counter: CounterReducer
  }
});

Redux store được tạo ra bằng function configureStore của @reduxjs/toolkit, configureStore nhận tham số truyền vào là object reducer, trong reducer khi bạn truyền object {counter: CounterSlice} có nghĩa là bạn muốn tạo ra một state.counter và sử dụng CounterReducer để thực hiện logic update state.counter.

2. Tạo Reducer và các Actions

import { createSlice } from "@reduxjs/toolkit";

export const counterSlice = createSlice({
  name: "counter",
  initialState: {
    value: 0
  },
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    }
  }
});

export const { increment, decrement } = counterSlice.actions;

export const selectCounter = (state) => state.counter;

export default counterSlice.reducer;

Function createSlice ở trong @reduxjs/toolkit cho phép chúng ta tạo một reducer với các actions tương ứng. Trong các tham số truyền vào createSlice thì initialState chỉ định giá trị state khi khởi tạo, option name dùng để chỉ định phần đầu của mỗi action, kết hợp với function increment ở trong reducers thì sẽ tạo ra một action có dạng {type: "counter/increment"}.

Lưu ý !

Trong reducers không cho phép thay đổi giá trị ban đầu của state (immutable), chỉ cho phép copies giá trị ban đầu rồi thay đổi giá trị vừa copies được đó. Các bạn có thể tìm hiểu thêm về Immutable trong Javasctipt tại đây

// ❌ cách này chính xác
state.value = 123
// ✅ Cách chính xác
return {
  ...state,
  value: 123
}

Bạn chỉ được phép thay đổi giá trị ban đầu của state bên trong createSlicecreateReducer

reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    }
  }

3. Tạo Counter Component

import { useDispatch, useSelector } from "react-redux";
import { increment, decrement, selectCounter } from "../Counter/CounterSlice";

export default function CounterComponent() {
  let counter = useSelector(selectCounter);
  let dispatch = useDispatch();

  return (
    <div>
      <p>{counter.value}</p>
      <button onClick={() => dispatch(increment())}>Increment</button>
      <button onClick={() => dispatch(decrement())}>Decrement</button>
    </div>
  );
}

Đầu tiên, useSelector giúp chúng ta lấy được một phần data từ Redux store, và selectCounter đã chỉ định state.counter là state sẽ được lấy ra, chúng ra có thể thấy nó ở trong file CounterSlice.js

export const selectCounter = (state) => state.counter;

Như chúng ta đã biết để dispatch một actions vào Redux store sẽ sử dụng như sau store.dispatch({ type: "counter/increment" }). Nhưng bây giờ, bạn chỉ cần sử dụng useDispatch

let dispatch = useDispatch();

Và khi có sự kiện click vào element, chúng ta sẽ dispatch một action

<button onClick={() => dispatch(increment()) }>
    Increment
</button>

4. Providing the Store

Chúng ta sử dụng useSelectoruseDispatch để gọi đến Redux store nhưng nếu bạn không import store, thì 2 hook sẽ gọi đến đâu ? Chúng ta cần phải sử dụng component <Provider> để truyền Redux store xuống để hook có thể truy cập vào.

import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import rootStore from "./app/rootStore";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={rootStore}>
    <App />
  </Provider>,
  rootElement
);

Và đây là kết quả 😀

Tổng kết

Mình mong rằng, với bài viết nho nhỏ của mình có thể giúp các bạn biết thêm về cách sử dụng Redux trong ứng dụng React. Các bạn có thể tìm hiểu kỹ hơn về React Redux tại trang doc của nó https://react-redux.js.org/.

Cảm ơn các bạn đã đọc bài viết 😘


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí