reactjs web

Redux trong ReactJS

1. Tổng quan Redux

Redux sẽ giải quyết một bài toán khá là quan trọng đó là chia sẻ state. Như đã đề cập ở trên thì việc chia sẻ state giữa các component với nhau theo các thức truyền qua props là khá phức tạp và rắc rối.

Giả sử khi chúng ta muốn truyền dữ liệu từ component A sang component C thì bắt buộc phải thông qua component B

Như hình bên trên khi chúng ta cần chia sẻ dữ liệu giữa các component với nhau bằng cách sử dụng props thì bắt buộc phải thông qua các component trung gian. Điều này khá phức tạp và dễ gây nhầm lẫn.

Để có thể truyền dữ liệu một cách tối ưu và đơn giản hơn chúng ta sẽ lưu dữ liệu vào một store, từ đó cấp phát dữ liệu cho các component cần thiết. Lúc này store sẽ đóng vai trò trung gian, nó có nhiệm vụ chứa và phân phát dữ liệu.

Giả sử bạn muốn chia sẽ dữ liệu từ component A tới component C thì chỉ cần đẩy state vào Store và Store sẽ cấp phát dữ liệu cho component C. Đây là mô hình mà Redux sử dụng, qua đó việc kiểm soát dữ liệu sẽ dễ dàng và tối ưu hơn

Store được coi là phần quan trọng nhất trong Redux, nó có nhiệm vụ lưu trữ và phân phát dữ liệu cho các component. Trong store bao gồm các thành phần như dispatcher (có nhiệm vụ kích hoạt các action bên trong reducer), reducer có nhiệm vụ xử lý các hành động được gửi đến. Sau đây là mô hình cách thức hoạt động của Redux.

Sau khi một action được thực thi, dispathcer sẽ được kích hoạt và gửi đến reducer một action. Lúc này reducer thực hiện hành động dựa vào action được gửi đến. Sau đó, đồng thời lưu lại giá trị của state mới vào trong store và trả về state mới đó.

Ví dụ ở đây mình có 1 đoạn code thực hiện tăng giảm giá trị của state thông qua redux.

import redux from 'redux';
//Reducer
const counter = (state = 0, action) => {
    //Kiểm tra điều kiện
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
    }
    return state;
};
 
//Đây là store
const store = redux.createStore(counter);
 
//Thực hiện dispath
store.dispath({type : 'INCREMENT'})

Ở ví dụ trên khi dispath được thực thi thì lúc này nó sẽ gửi đến cho reducer một action có type là INCREMENTreducer kiểm tra action và tiến hành tăng giá trị của state và trả về state mới.

2. Cài đặt Redux

Sau khi khởi tạo môt dự án ReactJS, để có thể sử dụng Redux chúng ta cần phải cài đặt 2 module là redux và react-redux bằng cách sử dụng npm:

npm install redux react-redux --save

3. Tích hợp Redux vào ReactJS

Ở đây chúng ta sẽ chia nhỏ các phần của Redux ra nhiều thư mục. Bạn có thể tìm hiểu về các thành phân quan trọng của redux ở bài viết giới thiệu về Redux ở trên. Chúng ta sẽ làm việc trong thư mục src

src/
....const/
        index.js
.....actions/
        index.js
....reducers/
        ...
        index.js
----components
        
....App.js

Mỗi thư mục sẽ có các nhiệm vụ khác nhau :

  • const: chứa các hằng số cố định của dự án.
  • actions: chứa các actions dùng để truyền vào hàm dispatch.
  • reducers: chứa các reducers trong redux.

Khởi tạo các hằng

Chúng ta sẽ đi khởi tạo các hằng số hỗ trợ việc triển khai dự án. Trong file const/index.js chúng ta sẽ khởi tạo 1 hằng số hỗ trợ việc thêm ghi chú.

// const/index.js
export const ADD_NEW_NOTE = "ADD_NEW_NOTE";

Khởi tạo actions

Actions là một object chứa các hành động mà bạn muốn gửi đến reducers. Giả sử như chúng ta muốn thêm note thì chúng ta sẽ chỉ định nó bên trong actions. Khi muốn gửi actions đến reducers chỉ cần gọi store.dispatch(actions). Ở đây chúng ta sẽ chỉ định các actions hỗ trợ việc thêm ghi chú như sau:

// actions/index.js
import { ADD_NEW_NOTE, REMOVE_NOTE, EDIT_NOTE } from "../const/index";
export const actAddNote = (content) => {
  return {
    type: ADD_NEW_NOTE,
    content,
  };
};

Mỗi action chúng ta cần phải chỉ định thụôc tính type có giá trị duy nhất. Bởi khi action gửi đến reducer nó sẽ dựa vafp thuộc tính action.type để xác định mình nên làm gì với state.

Khởi tạo reducers

Reducers sẽ có nhiệm vụ thay đổi state của ứng dụng dựa trên từng hành động được gửi đế. Trong các dự án lớn chúng ta cần chia ra rất nhiều reducers khác nhau. Ở trong thư mục src/reducers sẽ chỉ khởi tạo 1 reducers có tên noteReducer.

// reducers/noteReducers.js
import { ADD_NEW_NOTE, REMOVE_NOTE, EDIT_NOTE } from "../const/index";
 
const noteReducers = (state = [], action) => {
  switch (action.type) {
    case ADD_NEW_NOTE:
      const generateID = new Date().getTime();
      state = [...state, { id: generateID, content: action.content }];
      return state;
    default:
      return state;
  }
};
 
export default noteReducers

Chúng ta sẽ gộp các reducer lại với nhau bằng hàm combineReducer.

// src/reducers/index.js
 
import {combineReducers} from 'redux'
import noteReducers from './noteReducer'
 
//Ở đay chúng ta có thể gộp nhiều reducers
// Ở ví dụ này mình chỉ có 1 reducers là noteReducers
export default combineReducers({
    note: noteReducers
})

Tích hợp Redux

Sau khi đã tạo ra các thành phần cần thiết trong ứng dụng React chúng ta cần phải tạo Store lưu trữ state. Chúng ta sẽ làm việc với file src/index.js

// src/index.js
 
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
 
 
import { Provider } from "react-redux";
import { createStore } from "redux";
 
//Gọi reducers
import reducers from "./reducers/index";
//Tạo store
const store = createStore(reducers);
 
ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);
 
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Để các component khác có thể lấy dữ liệu chúng ta cần phải bọc các component vào trong Provider.

Lấy và cập nhật giá trị của state từ Store

Sau khi đã hoàn thành xong tất cả các bước cài đặt Redux cho project, chúng ta có thể thực hiên lấy và cập nhật giá trị của state ở store về component. Giả sử ở đây chúng ta muốn tương tác với store ở component App.js sẽ thực hiện như sau:

// src/App.js
 
//Import kết nối tới react-redux
import { connect } from 'react-redux'
//Import action dùng để dispatch
import {actAddNote} from './actions/index'
 
function App(props) {
   
  return (
    ...
  );
}
 
//Gán dispatch thành props
const mapDispatchToProps = (dispatch) =>  {
  return {
    addNote: (content) => {
      dispatch(actAddNote(content))
    }
  }
}
 
//Gán giá trị của state thành props
const mapStateToProps = (state, ownProps) => {
  return {
    note: state.note
  }
}
 
//Export component với két nối redux.
export default connect(mapStateToProps, mapDispatchToProps)(App)

Để kết nối với redux ở trong component chúng ta cần phải import hàm kết nối. Ở đây có 2 hàm cực kì quan trọng giúp thao tác với state đó là:

  • mapStateToProps: giúp chuyển state sang thành props sử dụng trong component.
  • mapDispatchToProps: giúp chuyển dispatch trong redux thành props. Giả sử mình muốn thực hiện dispatch action actAddNote thì mình chỉ cần gọi props.addNote()