Với những người mới học React thì Redux thực sự là một cơn ác mộng. Cũng giống như bạn đang code jQuery quen và chuyển sang React, đang chỉnh sửa DOM trực tiếp lại phải chuyển qua dùng state, …
Vậy để làm quen với Redux một cách nhanh nhất, chúng ta sẽ thử làm 1 ứng dụng đơn giản như sau:
Một website có 2 trang: Dashboard và Login.
Ở trang login cho phép nhập username, và khi chuyển qua trang Dashboard thì hiển thị username đó, nếu không nhập gì thì hiển thị là “Guest”.
Để tạo một website có nhiều page, mình sử dụng thêm thư viện React Router.
Trong ví dụ có 2 trang là Dashboard và Login, thì chúng ta tạo 2 function component tương ứng cho 2 trang, và cấu hình router (đường dẫn cho các trang) ở root component là App như sau:
Đoạn code trên có ý nghĩa: Nếu người dùng truy cập website theo đường dẫn “/login” thì sẽ hiển thị nội dung của Login component, “/dashboard” thì hiển thị nội dung của Dashboard component, còn các trường hợp khác thì sẽ chuyển về đường dẫn “/login” (redirect).
Component Login và Dashboard đặt trong thư mục pages và có nội dung đơn giản như sau:
// pages/Login.js
importReact, { useState } from"react";
import { useHistory } from"react-router-dom";
exportdefaultfunctionLogin() {
// Khởi tạo state lưu giá trị người dùng nhập vào ô input username
const [username, setUsername] =useState("");
// Sử dụng hook useHistory() của react-router-dom để chuyển hướng người dùng sang trang khác
consthistory=useHistory();
// Hàm xử lý khi người dùng bấm nút login
functionhandleLogin() {
// Chuyển hướng họ sang trang dashboard
history.push("/dashboard");
}
return (
<>
<h1>Login</h2>
<inputtype="text"placeholder="Username"value={username}
onChange={event => setUsername(event.target.value)}
/>{" "}
<buttononClick={handleLogin}>Login</button>
</>
);
}
Mục tiêu cần làm tiếp theo là khi người dùng nhập dữ liệu vào ô input ở trang Login sau đó bấm nút login thì chuyển qua trang Dashboard, và ở trang này hiển thị được giá trị vừa nhập chỗ “Welcome Guest”. Để làm được chức năng này thì có nhiều cách, và trong bài này thì mình sẽ hướng dẫn cách sử dụng Redux. Tham khảo thêm bài viết Truyền dữ liệu giữa các Component trong React.
Để truyền dữ liệu giữa các Component dễ hơn thì chúng ta dùng Redux, và cụ thể hơn trong ví dụ này chúng ta dùng Redux Toolkit để cấu hình nhanh hơn và dễ hơn dùng Redux Core.
Các bạn hình dung Redux Store như 1 nơi lưu state global mà tất cả các Component trong phạm vi của Store đều có thể truy xuất để lấy dữ liệu hoặc cập nhật dữ liệu. Giao diện của các Component đó cũng sẽ được tự động cập nhật khi state thay đổi.
Với các dự án lớn thì trong Store có thể chia ra nhiều Slice (nhóm các state theo chức năng). Với ví dụ đơn giản này thì chúng ta chỉ cần 1 Slice, tạo Slice là userSlice tương tự như sau:
// store/userSlice.js
import { createSlice } from"@reduxjs/toolkit";
// Khởi tạo state cho slice, có thể kèm giá trị mặc định ban đầu
constinitialState= {
username:"Guest", // State username với giá trị mặc định là "Guest"
// Có thể khai báo nhiều state khác nữa
};
// Cấu hình slice
exportconstuserSlice=createSlice({
name:"user", // Tên của slice, mỗi slice đặt 1 tên khác nhau để phân biệt
initialState,
// Reducers chứa các hàm xử lý cập nhật state
reducers: {
updateUsername: () => {},
},
});
// Export action ra để sử dụng cho tiện.
exportconst { updateUsername } =userSlice.actions;
// Action là 1 hàm trả về object dạng {type, payload}, chạy thử console.log(updateUsername()) để xem chi tiết
// Hàm giúp lấy ra state mong muốn.
// Hàm này có 1 tham số là root state là toàn bộ state trong store, chạy thử console.log(state) trong nội dung hàm để xem chi tiết
exportconstselectUsername= (state) => state.user.username;
// Export reducer để nhúng vào Store
exportdefaultuserSlice.reducer;
Tạo 1 file để cấu hình Store:
1
2
3
4
5
6
7
8
9
10
11
// store/index.js
import { configureStore } from"@reduxjs/toolkit";
importuserReducerfrom"./userSlice";
exportconststore=configureStore({
reducer: {
user:userReducer, // Khai báo 1 slide tên là user với giá trị là userReducer được export ở file userSlice
// Có thể khai báo nhiều slide khác tương tự
},
});
Khai báo phạm vi sử dụng Store, ở đây mình dùng cho toàn bộ website nên sẽ khai báo ở file index.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
importReactfrom"react";
importReactDOMfrom"react-dom";
import { Provider } from"react-redux";
import { store } from"./store/index";
importAppfrom"./App";
// Bọc App component vào trong Store Provider để App và toàn bộ Component con đều có thể truy xuất đến Store
ReactDOM.render(
<Providerstore={store}>
<App />
</Provider>,
document.getElementById("root")
);
importReact, { useState } from"react";
import { useHistory } from"react-router-dom";
// Import hook useDispatch từ react-redux và action updateUsername từ userSlice
import { useDispatch } from"react-redux";
import { updateUsername } from"../store/userSlice";
exportdefaultfunctionLogin() {
const [username, setUsername] =useState("");
consthistory=useHistory();
constdispatch=useDispatch();
functionhandleLogin() {
// Dispatch action updateUsername vào store, action này có payload (dữ liệu đi kèm) là username
dispatch(updateUsername(username));
history.push("/dashboard");
}
...
}
Sửa lại nội dung hàm updateUsername trong reducers của userSlice:
1
2
3
4
5
6
7
8
reducers: {
// Hàm có 2 tham số là state hiện tại và action truyền vào
updateUsername: (state, action) => {
// Cập nhật state username với giá trị truyền vào qua action (action.payload)
// Chạy thử console.log(action) để xem chi tiết giá trị action truyền vào
state.username=action.payload;
};
}
importReactfrom"react";
import { Link } from"react-router-dom";
// Import hook useSelector từ react-redux và hàm selectUsername từ userSlice
import { useSelector } from"react-redux";
import { selectUsername } from"../store/userSlice";
exportdefaultfunctionDashboard() {
// Lấy ra state username từ store
// Hàm useSelector cần truyền vào 1 hàm callback có tham số là root state và trả về state cần lấy
constusername=useSelector(selectUsername);
return (
<>
<h1>Dashboard</h2>
{/* In biến username ra màn hình */}
<h2>Welcome {username}</h2>
<Linkto="/login">Logout</Link>
</>
);
}