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.
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:
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
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
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
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 để cùng thảo luận nhé
Hẹn gặp bạn trong những bài tiếp theo.