Sử dụng Redux để quản lý state trong ứng dụng React
Bài đăng này đã không được cập nhật trong 3 năm
Giới thiệu
Ở bài viết trước, mình đã giới thiệu cho các bạn về thành phần và follow của Redux để quản lý state cho ứng dụng Javascript. Link bài viết (Làm quen với Redux để quản lý state cho ứng dụng Javascript) mọi người có thể xem lại để hiểu thêm về Redux nhé. Và trong bài viết này, mình sẽ sử dụng Redux để áp dụng vào ứng dụng React (một Javascript framework cho Front-end rất phổ biết hiện nay).
Thực hiện cài đặt
Trong vài viết này, mình sẽ sử dụng React để thực hiện tạo một ứng dụng đếm (counter) sử dụng kết hợp các Redux, Redux Toolkit và React-Redux.
1. Redux core
Chắc chắn rồi, để bắt đầu thì trước tiên các bạn cần cài đặt Redux
# sử dụng npm
npm install redux --save
# sử dụng yarn
yarn add redux --save
2. React Tokit
Redux-Toolkit được khuyết khích để viết Redux logic, giúp tránh các lỗi thường gặp và làm đơn giản hóa việc viết ứng dụng Redux
# sử dụng npm
npm install @reduxjs/toolkit --save
# sử dụng yarn
yarn add @reduxjs/toolkit --save
3. React-Redux
React-Redux là package giúp cho React Components của bạn có thể tương tác với Redux store bằng cách đọc state và dispatch actions để cập nhật state trong store.
# sử dụng npm
npm install react-redux --save
# sử dụng yarn:
yarn add react-redux --save
4. Sử dụng Create React App
Đây là cách được khuyến khích để bắt đầu một app React Redux bằng mẫu có sẵn kết hợp Redux + Redux Toolkit bằng câu lệnh Create React App
npx create-react-app my-app --template redux
Trong bài viết này mình khuyết khích nếu các bạn mới làm quen với React Redux, thì nên tự set up từ đầu để hiểu rỏ hơn cấu trúc của ứng dụng React Redux nhé.
Các bước thực hiện
1. Tạo Redux store
import { configureStore } from "@reduxjs/toolkit";
import CounterReducer from "../Features/Counter/CounterSlice";
export default configureStore({
reducer: {
counter: CounterReducer
}
});
Redux store được tạo ra bằng function configureStore
của @reduxjs/toolkit
, configureStore
nhận tham số truyền vào là object reducer
, trong reducer
khi bạn truyền object {counter: CounterSlice}
có nghĩa là bạn muốn tạo ra một state.counter
và sử dụng CounterReducer
để thực hiện logic update state.counter
.
2. Tạo Reducer và các Actions
import { createSlice } from "@reduxjs/toolkit";
export const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0
},
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
}
}
});
export const { increment, decrement } = counterSlice.actions;
export const selectCounter = (state) => state.counter;
export default counterSlice.reducer;
Function createSlice
ở trong @reduxjs/toolkit
cho phép chúng ta tạo một reducer
với các actions
tương ứng. Trong các tham số truyền vào createSlice
thì initialState
chỉ định giá trị state khi khởi tạo, option name
dùng để chỉ định phần đầu của mỗi action, kết hợp với function increment
ở trong reducers
thì sẽ tạo ra một action có dạng {type: "counter/increment"}
.
Lưu ý !
Trong reducers
không cho phép thay đổi giá trị ban đầu của state (immutable), chỉ cho phép copies
giá trị ban đầu rồi thay đổi giá trị vừa copies
được đó. Các bạn có thể tìm hiểu thêm về Immutable trong Javasctipt tại đây
// ❌ cách này chính xác
state.value = 123
// ✅ Cách chính xác
return {
...state,
value: 123
}
Bạn chỉ được phép thay đổi giá trị ban đầu của state bên trong createSlice
và createReducer
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
}
}
3. Tạo Counter Component
import { useDispatch, useSelector } from "react-redux";
import { increment, decrement, selectCounter } from "../Counter/CounterSlice";
export default function CounterComponent() {
let counter = useSelector(selectCounter);
let dispatch = useDispatch();
return (
<div>
<p>{counter.value}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
}
Đầu tiên, useSelector
giúp chúng ta lấy được một phần data từ Redux store, và selectCounter
đã chỉ định state.counter
là state sẽ được lấy ra, chúng ra có thể thấy nó ở trong file CounterSlice.js
export const selectCounter = (state) => state.counter;
Như chúng ta đã biết để dispatch một actions vào Redux store sẽ sử dụng như sau store.dispatch({ type: "counter/increment" })
. Nhưng bây giờ, bạn chỉ cần sử dụng useDispatch
let dispatch = useDispatch();
Và khi có sự kiện click vào element, chúng ta sẽ dispatch một action
<button onClick={() => dispatch(increment()) }>
Increment
</button>
4. Providing the Store
Chúng ta sử dụng useSelector
và useDispatch
để gọi đến Redux store nhưng nếu bạn không import store, thì 2 hook sẽ gọi đến đâu ? Chúng ta cần phải sử dụng component <Provider>
để truyền Redux store xuống để hook có thể truy cập vào.
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import rootStore from "./app/rootStore";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={rootStore}>
<App />
</Provider>,
rootElement
);
Và đây là kết quả
Tổng kết
Mình mong rằng, với bài viết nho nhỏ của mình có thể giúp các bạn biết thêm về cách sử dụng Redux trong ứng dụng React. Các bạn có thể tìm hiểu kỹ hơn về React Redux tại trang doc của nó https://react-redux.js.org/.
Cảm ơn các bạn đã đọc bài viết
All rights reserved