Nếu bạn chưa từng sử dụng Redux Toolkit thì có thể xem bài hướng dẫn này trước: https://techmaster.vn/posts/36409/huong-dan-su-dung-react-router-va-redux-toolkit
Bài viết này mình sẽ hướng dẫn cách thực hiện các async logic (xử lý bất đồng bộ) trong Redux Toolkit, cụ thể là sử dụng createAsyncThunk để kết nối với API login.
Ví dụ demo chúng ta có thể lấy luôn từ ví dụ trước rồi cải tiến thêm: https://stackblitz.com/edit/react-router-redux-toolkit?file=src/index.js.
Đầu tiên là bổ sung giao diện để có thêm ô nhập mật khẩu vì khi gọi API sẽ cần có email và password.
Bây giờ khi người dùng bấm nút Login, chúng ta sẽ gọi vào API để thực hiện login. Để gọi API thì chúng ta dùng luôn hàm fetch() có sẵn. Lệnh fetch() là một lệnh async, nên sẽ không viết code logic vào trong action để cập nhật state như trước, mà chúng ta sẽ dùng hàm createAsyncThunk để tạo ra các async action.
Ví dụ tạo một async action (và export luôn ra ngoài để sử dụng ở Component):
export const login = createAsyncThunk(
// Tên action
'user/login',
// Code async logic, tham số đầu tiên data là dữ liệu truyền vào khi gọi action
async (data, { rejectWithValue }) => {
// Gọi lên API backend
const response = await fetch(
'https://fake-rest-api-nodejs.herokuapp.com/login',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}
);
// Convert dữ liệu ra json
const jsonData = await response.json();
// Nếu bị lỗi thì reject
if (response.status < 200 || response.status >= 300) {
return rejectWithValue(jsonData);
}
// Còn không thì trả về dữ liệu
return jsonData;
}
);
Ví dụ trên sẽ tạo 1 request đến API backend: https://fake-rest-api-nodejs.herokuapp.com/login, API này sẽ trả về thông tin user nếu đăng nhập thành công. Các bạn có thể tự tạo một API riêng bằng cách dùng tool này: https://github.com/robinhuy/fake-rest-api-nodejs (xem hướng dẫn Tiếng Việt tại đây: https://techmaster.vn/posts/35578/tao-1-rest-api-phuc-vu-cho-muc-dich-hoc-tap-trong-30-giay).
Tiếp theo chúng ta sẽ tạo ra các extra Reducers dùng để xử lý các trạng thái của async action ở trên:
// Tạo login action (async)
export const login = createAsyncThunk('user/login', {
// Code như trên
})
export const userSlice = createSlice({
name: 'user',
// Thêm 1 số state như trạng thái loading, báo lỗi và thông tin user đang đăng nhập
initialState: {
isLoading: false,
errorMessage: '',
currentUser: null,
},
// Các action bình thường (sync action)
reducers: {
// Logout không gọi API mà chỉ đơn giản là cập nhật state
logout: (state) => {
state.currentUser = null;
state.errorMessage = '';
},
},
// Code logic xử lý async action
extraReducers: (builder) => {
// Bắt đầu thực hiện action login (Promise pending)
builder.addCase(login.pending, (state) => {
// Bật trạng thái loading
state.isLoading = true;
});
// Khi thực hiện action login thành công (Promise fulfilled)
builder.addCase(login.fulfilled, (state, action) => {
// Tắt trạng thái loading, lưu thông tin user vào store
state.isLoading = false;
state.currentUser = action.payload;
});
// Khi thực hiện action login thất bại (Promise rejected)
builder.addCase(login.rejected, (state, action) => {
// Tắt trạng thái loading, lưu thông báo lỗi vào store
state.isLoading = false;
state.errorMessage = action.payload.message;
});
},
});
Sau khi code xong action thì chúng ta dispatch như bình thường. Ví dụ truyền vào 1 object có chứa email và password:
<button onClick={() => dispatch(login({ email, password }))}>
Login
</button>
Sửa lại thêm một số logic khi có thêm các state như isLoading, currentUser, errorMessage, cái này thì các bạn hãy thử tự làm vì cách lấy state ra vẫn như cũ.
Tham khảo toàn bộ code mẫu tại đây: https://stackblitz.com/edit/react-router-redux-toolkit-fetch-api?file=src/App.js.
Happy coding 😎
Bình luận
Bài viết rất chi tiết!
Em để ý hàm useHistory() giúp điều hướng trang web sang màn khác sau khi nhấn đăng nhập ạ
Ở công ty em làm gọi API với extraReducer suốt anh!