Chào mọi người, trong bài viết này mình sẽ hướng dẫn bạn cách sử dụng Redux Thunk kết hợp cùng React Hooks API.

Để bạn học ReactJS dễ dàng hơn thì mình sẽ làm những ví dụ nhỏ để bạn nắm rõ hơn về những kỹ thuật xử lý và kết hợp với một số library thường được sử dụng.

Contents

Khởi tạo project và cài package

Đầu tiên là chúng ta tạo một project mới bằng lệnh yarn create react-app redux-thunk-hooks

Sau khi đã tạo xong thì bạn truy cập vào project bằng lệnh cd redux-thunk-hooks và tiến hành cài 3 package redux, react-redux, redux-thunk bằng lệnh

yarn add redux react-redux redux-thunk

Sau đó thì cài thêm redux-logger bằng lệnh yarn add redux-logger --dev để chỉ cài cho môi trường dev thôi.

Sau khi add xong thì file package.json của bạn sẽ trông thế này

{
  "name": "with-redux-thunk",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-redux": "^7.2.1",
    "react-scripts": "3.4.3",
    "redux": "^4.0.5",
    "redux-thunk": "^2.3.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "redux-logger": "^3.0.6"
  }
}

Các bạn lưu ý về phiên bản của Redux thì từ bản 7.1 trở lên thì mới hỗ trợ Hooks nha.

Thêm Redux vào project

Tiếp theo các bạn tạo một thư mục src/redux, src/redux/reducers, src/redux/actions, src/redux/constants và một file src/redux/store.js

Trong ví dụ lần này mình sẽ tạo một project để show một list posts get từ api về.

Sau đó tạo các file bên trong các thư mục lần lượt actions/postAction.js, reducers/postReducer.js, constants/post.js

Sau khi hoàn tất thì cấu trúc project sẽ như sau:

Folder Structure
Folder Structure

Chúng ta sẽ tạo những action name ở trong file constant như sau

// post.js
export const FETCH_POST_REQUEST = "FETCH_POST_REQUEST";
export const FETCH_POST_SUCCESS = "FETCH_POST_SUCCESS";
export const FETCH_POST_ERROR = "FETCH_POST_ERROR";

Chúng ta mở file postReducer.js ra

// import constants
import {
    FETCH_POST_REQUEST,
    FETCH_POST_SUCCESS,
    FETCH_POST_ERROR,
} from '../constants/post';

// khởi tạo một init state
const initialState = {
    requesting: false,
    success: false,
    message: null,
    data: null
}

// bắt từng action type
function exampleReducers(state = initialState, payload) {
    switch (payload.type) {
        case FETCH_EXAMPLE_REQUEST:
            return {
                ...state,
                requesting: true
            };
        case FETCH_EXAMPLE_SUCCESS:
            return {
                ...state,
                requesting: false,
                success: true,
                data: payload.data
            };
        case FETCH_EXAMPLE_ERROR:
            return {
                ...state,
                requesting: false,
                message: payload.message
            };

        default:
            return state;
    }
}

export default exampleReducers;

Tiếp theo chúng ta viết action

Chúng ta dispatch với type FETCH_POST_REQUEST sau đó call api

Khi api đã call xong thì lại dispatch đến FETCH_POST_SUCCESS và kèm theo data responseBody

Trường hợp lỗi thì nó sẽ tự chạy vào catch và sẽ dispatch đến FETCH_POST_ERROR

// postAction.js
import {
    FETCH_POST_REQUEST,
    FETCH_POST_SUCCESS,
    FETCH_POST_ERROR,
} from '../constants/POST';

export const loadPosts = () => async dispatch => {
    try {
        dispatch({ type: FETCH_POST_REQUEST });

        const url = "https://jsonplaceholder.typicode.com/posts";
        const response = await fetch(url)
        const responseBody = await response.json();
        dispatch({
            type: FETCH_POST_SUCCESS,
            data: responseBody
        });
    } catch (error) {
        console.error(error);
        dispatch({
            type: FETCH_POST_ERROR,
            message: error
        });
    }
}

Sau đó sẽ tạo một file reducers/index.js để làm main reducers

// reducers/index.jsjs
import { combineReducers } from 'redux';
import postReducer from './postReducer';

const reducers = combineReducers({
	posts: postReducer,
});

export default (state, action) => reducers(state, action);

Tiếp theo chúng ta cùng viết store, về lý thuyết của store mình sẽ không nói lại ở đây nữa nhé.

import { createStore, applyMiddleware } from 'redux'
import { createLogger } from 'redux-logger'
import thunk from 'redux-thunk'
import reducer from './reducers'

const middleware = [ thunk ];
// check nếu không phải production thì push logger vào để log ra những action
if (process.env.NODE_ENV !== 'production') {
  middleware.push(createLogger());
}

export const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)

Kết nối redux vào app

Sau khi đã xong phần redux thì bạn hãy mở file App.js lên và import 2 thằng

import { useDispatch, useSelector } from 'react-redux';
import { loadPosts } from './redux/actions/postAction';

Tiếp tục trong phần app function chúng ta viết một hàm useEffect và đồng thời khai báo useDispatch để sử dụng

Chi tiết bạn có thể xem về react redux hooks

const dispatch = useDispatch();
  useEffect(() => {
    dispatch(loadPosts());
  }, [dispatch]);

Bạn vẫn gọi dispatch như bình thường, nhưng với hooks thì bạn sẽ sử dụng useDispatch để kết nói với redux.

Còn với useEffect nó sẽ chạy mỗi khi update, nó sẽ tương ứng với các hàm cũ componentDidMount, componentDidUpdate, componentWillUnmount

Tiếp theo chúng ta sẽ gọi hook useSelector ra để lấy state tương tự việc bạn mapState

Chi tiết về nó bạn có thể xem tại đây

const data = useSelector((state) => state.posts.data);
  const requesting = useSelector((state) => state.posts.requesting);

Tất cả đã xong mình chuyển xuống dưới để show data ra như sau

return (
    <div className="App">
      <header className="App-header">
        {
          requesting ?
            <img src={logo} className="App-logo" alt="logo" />
            :
            (data && data.length > 0) ? <div>
              <ul className="list-group">
                {data.map(item =>
                  <li key={item.id} className="list-group-item">{item.title}</li>
                )}
              </ul>
            </div>
              : <div>Data is empty</div>
        }
      </header>
    </div>
  );

Toàn bộ file App.js của mình sẽ như thế này

import React, { useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { loadPosts } from './redux/actions/postAction';

function App() {
  const data = useSelector((state) => state.post.data);
  const requesting = useSelector((state) => state.post.requesting);

  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(loadPosts());
  }, [dispatch]);

  return (
    <div className="App">
      <header className="App-header">
        {
          requesting ?
            <img src={logo} className="App-logo" alt="logo" />
            :
            (data && data.length > 0) ? <div>
              <ul className="list-group">
                {data.map(item =>
                  <li key={item.id} className="list-group-item">{item.title}</li>
                )}
              </ul>
            </div>
              : <div>Data is empty</div>
        }
      </header>
    </div>
  );
}

export default App;

Ok giờ chạy thử thôi yarn start và mở cổng 300 ra xem nó ra cái gì nào

Redux thunk hooks result
Redux thunk hooks result

Vậy là ok không có lỗi lầm gì cả rồi nhé.

Kết luận

Như vậy qua bài này mình đã hướng dẫn bạn sử dụng Redux Thunk Hooks. Nó không có gì khác nhiều so với sử dụng bình thường, chỉ cần thay một số chỗ là ok

Nếu có bất kỳ thắc mắc nào bạn có thể comment ở bên dưới, hoặc join group Facebook để cùng thảo luận nhé

Hẹn gặp bạn trong những bài tiếp theo.