Cách quản lý trạng thái trong React với Redux
Redux là một repodata phổ biến cho các ứng dụng JavaScript và React . Nó tuân theo một nguyên tắc trung tâm rằng ràng buộc dữ liệu phải chảy theo một hướng và phải được lưu trữ như một nguồn sự thật duy nhất. Redux trở nên phổ biến vì sự đơn giản của ý tưởng thiết kế và việc triển khai tương đối nhỏ.Redux hoạt động theo một vài khái niệm. Đầu tiên, cửa hàng là một đối tượng duy nhất với các trường cho mỗi lựa chọn dữ liệu. Bạn cập nhật dữ liệu bằng cách gửi một hành động cho biết dữ liệu sẽ thay đổi như thế nào. Sau đó, bạn diễn giải các hành động và cập nhật dữ liệu bằng cách sử dụng bộ giảm . Bộ giảm thiểu là các hàm áp dụng các hành động cho dữ liệu và trả về một trạng thái mới, thay vì thay đổi trạng thái trước đó.
Trong các ứng dụng nhỏ, bạn có thể không cần repodata global . Bạn có thể sử dụng kết hợp giữa trạng thái và ngữ cảnh cục bộ để quản lý trạng thái. Nhưng khi ứng dụng của bạn mở rộng quy mô, bạn có thể gặp phải các tình huống mà việc lưu trữ thông tin một cách tập trung sẽ rất có giá trị để nó tồn tại trên các tuyến vàthành phần . Trong tình huống đó, Redux sẽ cung cấp cho bạn một cách chuẩn để lưu trữ và truy xuất dữ liệu một cách có tổ chức.
Trong hướng dẫn này, bạn sẽ sử dụng Redux trong ứng dụng React bằng cách xây dựng một ứng dụng thử nghiệm xem chim. User sẽ có thể thêm các loài chim mà họ đã nhìn thấy và tăng thêm một con chim mỗi khi họ nhìn thấy lại. Bạn sẽ xây dựng một cửa hàng dữ liệu duy nhất và bạn sẽ tạo các hành động và trình giảm bớt để cập nhật cửa hàng. Sau đó, bạn sẽ kéo dữ liệu vào các thành phần của bạn và gửi các thay đổi mới để cập nhật dữ liệu.
Yêu cầu
Bạn cần một môi trường phát triển chạy Node.js ; hướng dẫn này đã được thử nghiệm trên Node.js version 10.22.0 và npm version 6.14.6. Để cài đặt phần mềm này trên macOS hoặc Ubuntu 18.04, hãy làm theo các bước trong Cách cài đặt Node.js và Tạo Môi trường Phát triển Cục bộ trên macOS hoặc phần Cài đặt Sử dụng PPA của Cách Cài đặt Node.js trên Ubuntu 18.04 .
Môi trường phát triển React được cài đặt với Create React App , với phần trình bày không cần thiết bị xóa. Để cài đặt điều này, hãy làm theo Bước 1 - Tạo một dự án trống của hướng dẫn Cách quản lý trạng thái trên các thành phần lớp React . Hướng dẫn này sẽ sử dụng
redux-tutorial
làm tên dự án.Bạn sẽ sử dụng các thành phần React, Hook và các biểu mẫu trong hướng dẫn này, bao gồm
useState
Hook và Hooks tùy chỉnh. Bạn có thể tìm hiểu về các thành phần và Hook trong hướng dẫn của ta Cách quản lý trạng thái với Hook trên các thành phần trong React và Cách xây dựng các Form trong React .Bạn cũng cần kiến thức cơ bản về JavaScript, HTML và CSS, bạn có thể tìm thấy kiến thức này trong loạt bài Cách tạo trang web bằng HTML , loạt bài Cách tạo trang web bằng CSS và trong loạt bài Cách viết mã bằng JavaScript .
Bước 1 - Cài đặt cửa hàng
Trong bước này, bạn sẽ cài đặt Redux và kết nối nó vớithành phần root của bạn.Sau đó, bạn sẽ tạo một cửa hàng cơ sở và hiển thị thông tin trong thành phần của bạn . Đến cuối bước này, bạn sẽ có một version Redux đang hoạt động với thông tin hiển thị trong các thành phần của bạn.
Để bắt đầu, hãy cài đặt redux
và react-redux
. Gói redux
là bất khả tri của khung và sẽ kết nối các hành động và trình giảm bớt của bạn. Gói react-redux
chứa các ràng buộc để chạy Redux store trong một dự án React. Bạn sẽ sử dụng mã từ react-redux
để gửi các hành động từ các thành phần của bạn và để kéo dữ liệu từ cửa hàng vào các thành phần của bạn.
Sử dụng npm
để cài đặt hai gói bằng lệnh sau:
- npm install --save redux react-redux
Khi thành phần được cài đặt xong, bạn sẽ nhận được kết quả như thế này. Đầu ra của bạn có thể hơi khác một chút:
Output... + redux@4.0.5 + react-redux@7.2.1 added 2 packages from 1 contributor, updated 1 package and audited 1639 packages in 20.573s
Đến đây bạn đã cài đặt các gói, bạn cần kết nối Redux với dự án của bạn . Để sử dụng Redux, bạn cần phải bọc các thành phần root của bạn bằng Trình Provider
đảm bảo rằng cửa hàng có sẵn cho tất cả các thành phần con trong cây. Điều này tương tự như cách bạn thêm Provider
bằng cách sử dụng context
root của React .
Mở src/index.js
:
- nano src/index.js
Nhập thành phần Provider
từ gói react-redux
. Thêm Provider
vào thành phần root của bạn xung quanh bất kỳ thành phần nào khác bằng cách áp dụng các thay đổi được đánh dấu sau cho mã của bạn:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './components/App/App'; import * as serviceWorker from './serviceWorker'; import { Provider } from 'react-redux'; ReactDOM.render( <React.StrictMode> <Provider> <App /> </Provider> </React.StrictMode>, document.getElementById('root') ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
Đến đây bạn đã gói các thành phần của bạn , đã đến lúc thêm một store
. store
là tập hợp dữ liệu trung tâm của bạn. Trong bước tiếp theo, bạn sẽ học cách tạo các bộ reducers
sẽ đặt các giá trị mặc định và cập nhật cửa hàng của bạn, nhưng bây giờ bạn sẽ mã hóa dữ liệu.
Nhập hàm createStore
từ redux
, sau đó truyền một hàm trả về một đối tượng . Trong trường hợp này, trả về một đối tượng có trường gọi là các birds
trỏ đến một mảng các con chim riêng lẻ. Mỗi con chim sẽ có một name
và số views
. Lưu kết quả của hàm số tới một giá trị gọi là store
, sau đó vượt qua các store
để một prop gọi là store
ở Provider
:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './components/App/App'; import * as serviceWorker from './serviceWorker'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; const store = createStore(() => ({ birds: [ { name: 'robin', views: 1 } ] })); ReactDOM.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.getElementById('root') ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
Lưu và đóng file . Đến đây bạn có một số dữ liệu, bạn cần có thể hiển thị nó. Mở src/components/App/App.js
:
- nano src/components/App/App.js
Giống như với context
, mọi thành phần con sẽ có thể truy cập cửa hàng mà không cần thêm bất kỳ đạo cụ nào. Để truy cập các mục trong cửa hàng Redux của bạn, hãy sử dụng Hook có tên là useSelector
từ useSelector
react-redux
. useSelector
Hook nhận một hàm bộ chọn làm đối số. Hàm bộ chọn sẽ nhận trạng thái cửa hàng của bạn dưới dạng đối số mà bạn sẽ sử dụng để trả về trường bạn muốn:
import React from 'react'; import { useSelector } from 'react-redux'; import './App.css'; function App() { const birds = useSelector(state => state.birds); return <></> } export default App;
Vì useSelector
là một Hook tùy chỉnh, thành phần sẽ hiển thị lại khi nào Hook được gọi. Điều đó nghĩa là dữ liệu— birds
luôn được cập nhật.
Đến đây bạn đã có dữ liệu, bạn có thể hiển thị nó trong một danh sách không có thứ tự. Tạo một <div>
xung quanh bằng className
của wrapper
. Bên trong, thêm một phần tử <ul>
và lặp qua mảng birds
với map()
, trả về một mục <li>
cho mỗi. Đảm bảo sử dụng bird.name
làm key
:
import React from 'react'; import { useSelector } from 'react-redux' import './App.css'; function App() { const birds = useSelector(state => state.birds); return ( <div className="wrapper"> <h1>Bird List</h1> <ul> {birds.map(bird => ( <li key={bird.name}> <h3>{bird.name}</h3> <div> Views: {bird.views} </div> </li> ))} </ul> </div> ); } export default App;
Lưu các file . Sau khi file được lưu, trình duyệt sẽ reload và bạn sẽ tìm thấy danh sách chim của bạn ::
Đến đây bạn đã có một danh sách cơ bản, hãy thêm phần còn lại của các thành phần bạn cần cho ứng dụng xem chim của bạn . Đầu tiên, hãy thêm một nút để tăng lượt xem sau danh sách các lượt xem:
import React from 'react'; import { useSelector } from 'react-redux' import './App.css'; function App() { const birds = useSelector(state => state.birds); return ( <div className="wrapper"> <h1>Bird List</h1> <ul> {birds.map(bird => ( <li key={bird.name}> <h3>{bird.name}</h3> <div> Views: {bird.views} <button><span role="img" aria-label="add">➕</span></button> </div> </li> ))} </ul> </div> ); } export default App;
Tiếp theo, tạo một <form>
với một <input>
duy nhất trước danh sách chim để user có thể thêm một con chim mới. Đảm bảo bao quanh <input>
bằng <label>
và thêm một type
submit
vào nút thêm đảm bảo mọi thứ đều có thể truy cập được:
import React from 'react'; import { useSelector } from 'react-redux' import './App.css'; function App() { const birds = useSelector(state => state.birds); return ( <div className="wrapper"> <h1>Bird List</h1> <form> <label> <p> Add Bird </p> <input type="text" /> </label> <div> <button type="submit">Add</button> </div> </form> <ul> {birds.map(bird => ( <li key={bird.name}> <h3>{bird.name}</h3> <div> Views: {bird.views} <button><span role="img" aria-label="add">➕</span></button> </div> </li> ))} </ul> </div> ); } export default App;
Lưu và đóng file . Tiếp theo, mở App.css
để thêm một số kiểu:
- nano src/components/App/App.css
Thêm một số padding
vào lớp wrapper
. Sau đó viết hoa phần tử h3
, phần tử chứa tên loài chim. Cuối cùng, tạo kiểu cho các node . Loại bỏ các kiểu nút mặc định trên thêm <button>
và sau đó thêm lề vào biểu mẫu <button>
.
Thay thế nội dung của file bằng nội dung sau:
.wrapper { padding: 20px; } .wrapper h3 { text-transform: capitalize; } .wrapper form button { margin: 10px 0; cursor: pointer; } .wrapper ul button { background: none; border: none; cursor: pointer; }
Ngoài ra, cung cấp cho mỗi nút một cursor
pointer
, pointer
này sẽ thay đổi con trỏ khi di chuột qua nút để cho user biết rằng nút có thể nhấp được.
Lưu và đóng file . Khi bạn thực hiện, trình duyệt sẽ làm mới với các thành phần của bạn:
Các node và biểu mẫu chưa được kết nối với bất kỳ hành động nào và do đó không thể tương tác với cửa hàng Redux. Bạn sẽ thêm các hành động ở Bước 2 và kết nối chúng ở Bước 3.
Trong bước này, bạn đã cài đặt Redux và tạo một cửa hàng mới cho ứng dụng của bạn . Bạn đã kết nối cửa hàng với ứng dụng của bạn bằng cách sử dụng Provider
và truy cập các phần tử bên trong các thành phần của bạn bằng useSelector
Hook.
Trong bước tiếp theo, bạn sẽ tạo các hành động và bộ giảm bớt để cập nhật thông tin mới cho cửa hàng của bạn .
Bước 2 - Tạo Hành động và Giảm bớt
Tiếp theo, bạn sẽ tạo các hành động để thêm một con chim và tăng chế độ xem. Sau đó, bạn sẽ thực hiện một trình giảm bớt sẽ cập nhật thông tin tùy thuộc vào loại hành động. Cuối cùng, bạn sẽ sử dụng các bộ giảm để tạo một cửa hàng mặc định bằng cách sử dụng bộ combineReducers
.
Hành động là thông điệp bạn gửi đến repodata với sự thay đổi dự định. Giảm bớt nhận các thông báo đó và cập nhật repository được chia sẻ bằng cách áp dụng các thay đổi tùy thuộc vào loại hành động. Các thành phần của bạn sẽ gửi các hành động mà họ muốn cửa hàng của bạn sử dụng và các thành phần của bạn sẽ sử dụng các hành động để cập nhật dữ liệu trong cửa hàng. Bạn không bao giờ gọi trực tiếp bộ giảm và có những trường hợp một hành động có thể tác động đến nhiều bộ giảm.
Có nhiều tùy chọn khác nhau để tổ chức các hành động và bộ giảm của bạn . Trong hướng dẫn này, bạn sẽ sắp xếp theo domain . Điều đó nghĩa là các hành động và bộ giảm của bạn sẽ được xác định theo loại tính năng mà chúng sẽ tác động.
Tạo một folder có tên là store
:
- mkdir src/store
Thư mục này sẽ chứa tất cả các hành động và trình giảm bớt của bạn. Một số mẫu lưu trữ chúng cùng với các thành phần, nhưng lợi thế ở đây là bạn có một điểm tham chiếu riêng cho hình dạng của toàn bộ cửa hàng. Khi một nhà phát triển mới tham gia vào dự án, họ sẽ có thể đọc sơ qua cấu trúc của cửa hàng.
Tạo một folder có tên là birds
bên trong folder store
. Điều này sẽ chứa các hành động và bộ giảm cụ thể để cập nhật dữ liệu về chim của bạn:
- mkdir src/store/birds
Tiếp theo, mở một file có tên là birds.js
để bạn có thể bắt đầu thêm các hành động và bộ giảm bớt. Nếu bạn có một số lượng lớn các hành động và bộ giảm birds.reducers.js
, bạn có thể cần chia chúng thành các file riêng biệt, chẳng hạn như birds.actions.js
và birds.reducers.js
, nhưng khi chỉ có một vài thao tác, bạn có thể dễ dàng đọc chúng hơn khi chúng ở cùng một vị trí:
- nano src/store/birds/birds.js
Đầu tiên, bạn sẽ tạo các hành động. Hành động là những thông báo mà bạn gửi từ một thành phần đến cửa hàng của bạn bằng một phương thức được gọi là dispatch
mà bạn sẽ sử dụng trong bước tiếp theo.
Một hành động phải trả về một đối tượng có trường type
. Nếu không, đối tượng trả về có thể bao gồm bất kỳ thông tin bổ sung nào bạn muốn gửi.
Tạo một hàm có tên addBirds
lấy một bird
làm đối số và trả về một đối tượng có chứa type
'ADD_BIRD'
và bird
dưới dạng một trường:
export function addBird(bird) { return { type: 'ADD_BIRD', bird, } }
Lưu ý bạn đang xuất hàm để sau này bạn có thể nhập và gửi nó từ thành phần của bạn .
Trường type
rất quan trọng để giao tiếp với các bộ giảm, vì vậy theo quy ước, hầu hết các cửa hàng Redux sẽ lưu kiểu vào một biến để bảo vệ khỏi lỗi chính tả.
Tạo một const
gọi ADD_BIRD
rằng tiết kiệm chuỗi 'ADD_BIRD'
. Sau đó, cập nhật hành động:
const ADD_BIRD = 'ADD_BIRD'; export function addBird(bird) { return { type: ADD_BIRD, bird, } }
Đến đây bạn có một hành động, hãy tạo một công cụ giảm thiểu sẽ phản hồi lại hành động đó.
Bộ giảm thiểu là các chức năng sẽ xác định cách một trạng thái sẽ thay đổi dựa trên các hành động. Các hành động không tự tạo ra thay đổi; bộ giảm sẽ tiếp nhận trạng thái và áp dụng các thay đổi dựa trên các hành động.
Một bộ giảm thiểu nhận được hai đối số: trạng thái hiện tại và hành động. Trạng thái hiện tại đề cập đến trạng thái cho một phần cụ thể của cửa hàng. Nói chung, tên của bộ giảm tốc sẽ trùng với một trường trong cửa hàng. Ví dụ: giả sử bạn có một cửa hàng có hình dạng như sau:
{ birds: [ // collection of bird objects ], gear: { // gear information } }
Bạn sẽ tạo ra hai bộ giảm tốc: birds
và gear
. state
cho bộ giảm birds
sẽ là mảng các loài chim. state
của gear
giảm tốc sẽ là đối tượng chứa thông tin bánh răng.
Inside birds.js
tạo một công cụ giảm thiểu có tên là những birds
có state
và action
và trả về state
mà không có bất kỳ thay đổi nào:
const ADD_BIRD = 'ADD_BIRD'; export function addBird(bird) { return { type: ADD_BIRD, bird, } } function birds(state, action) { return state; }
Lưu ý bạn không xuất bộ giảm tốc. Bạn sẽ không trực tiếp sử dụng bộ giảm và thay vào đó sẽ kết hợp chúng thành một bộ sưu tập có thể sử dụng được mà bạn sẽ xuất và sử dụng để tạo cửa hàng cơ sở của bạn trong index.js
. Cũng lưu ý bạn cần phải trả lại state
nếu không có thay đổi. Redux sẽ chạy tất cả các trình giảm bớt khi nào bạn gửi một hành động, vì vậy nếu bạn không trả về trạng thái, bạn có nguy cơ mất các thay đổi của bạn .
Cuối cùng, vì Redux trả về trạng thái nếu không có thay đổi, hãy thêm trạng thái mặc định bằng các tham số mặc định .
Tạo một mảng defaultBirds
sẽ có thông tin về chim giữ chỗ. Sau đó cập nhật state
để bao gồm defaultBirds
làm tham số mặc định:
const ADD_BIRD = 'ADD_BIRD'; export function addBird(bird) { return { type: ADD_BIRD, bird, } } const defaultBirds = [ { name: 'robin', views: 1, } ]; function birds(state=defaultBirds, action) { return state; }
Đến đây bạn có một bộ giảm tốc trả về trạng thái của bạn , bạn có thể sử dụng hành động để áp dụng các thay đổi. Mẫu phổ biến nhất là sử dụng switch
trên action.type
để áp dụng các thay đổi.
Tạo một câu lệnh switch
sẽ xem xét action.type
. Nếu trường hợp là ADD_BIRD
, hãy trải trạng thái hiện tại thành một mảng mới và thêm con chim với một chế độ xem duy nhất:
const ADD_BIRD = 'ADD_BIRD'; export function addBird(bird) { return { type: ADD_BIRD, bird, } } const defaultBirds = [ { name: 'robin', views: 1, } ]; function birds(state=defaultBirds, action) { switch (action.type) { case ADD_BIRD: return [ ...state, { name: action.bird, views: 1 } ]; default: return state; } }
Lưu ý bạn đang trả về state
làm giá trị default
. Quan trọng hơn, bạn không đột biến state
trực tiếp. Thay vào đó, bạn đang tạo một mảng mới bằng cách trải rộng mảng cũ và thêm một giá trị mới.
Đến đây bạn đã có một hành động, bạn có thể tạo một hành động để tăng một lượt xem.
Tạo một hành động có tên là incrementBird
. Giống như hành động addBird
, hành động này sẽ lấy một con chim làm đối số và trả về một đối tượng với một type
và một bird
. Sự khác biệt duy nhất là loại sẽ là 'INCREMENT_BIRD'
:
const ADD_BIRD = 'ADD_BIRD'; const INCREMENT_BIRD = 'INCREMENT_BIRD'; export function addBird(bird) { return { type: ADD_BIRD, bird, } } export function incrementBird(bird) { return { type: INCREMENT_BIRD, bird } } const defaultBirds = [ { name: 'robin', views: 1, } ]; function birds(state=defaultBirds, action) { switch (action.type) { case ADD_BIRD: return [ ...state, { name: action.bird, views: 1 } ]; default: return state; } }
Hành động này là riêng biệt, nhưng bạn sẽ sử dụng cùng một bộ giảm tốc. Lưu ý , các hành động truyền tải thay đổi bạn muốn thực hiện trên dữ liệu và trình giảm áp dụng những thay đổi đó để trả về trạng thái mới.
Việc tăng cường một con chim bao gồm nhiều thứ hơn là thêm một con chim mới. Bên trong những birds
thêm một hộp INCREMENT_BIRD
mới cho INCREMENT_BIRD
. Sau đó, kéo con chim bạn cần tăng ra khỏi mảng bằng cách sử dụng find()
để so sánh từng name
với action.bird
:
const ADD_BIRD = 'ADD_BIRD'; ... function birds(state=defaultBirds, action) { switch (action.type) { case ADD_BIRD: return [ ...state, { name: action.bird, views: 1 } ]; case INCREMENT_BIRD: const bird = state.find(b => action.bird === b.name); return state; default: return state; } }
Bạn có con chim bạn cần thay đổi, nhưng bạn cần trả lại trạng thái mới chứa tất cả các loài chim không thay đổi cũng như con chim bạn đang cập nhật. Chọn tất cả các con chim còn lại bằng state.filter
bằng cách chọn tất cả các con có name
không bằng action.name
. Sau đó, trả lại một mảng mới bằng cách mở rộng mảng birds
và thêm bird
vào cuối:
const ADD_BIRD = 'ADD_BIRD'; ... function birds(state=defaultBirds, action) { switch (action.type) { case ADD_BIRD: return [ ...state, { name: action.bird, views: 1 } ]; case INCREMENT_BIRD: const bird = state.find(b => action.bird === b.name); const birds = state.filter(b => action.bird !== b.name); return [ ...birds, bird, ]; default: return state; } }
Cuối cùng, cập nhật chú bird
bằng cách tạo một đối tượng mới với view
tăng dần:
const ADD_BIRD = 'ADD_BIRD'; ... function birds(state=defaultBirds, action) { switch (action.type) { case ADD_BIRD: return [ ...state, { name: action.bird, views: 1 } ]; case INCREMENT_BIRD: const bird = state.find(b => action.bird === b.name); const birds = state.filter(b => action.bird !== b.name); return [ ...birds, { ...bird, views: bird.views + 1 } ]; default: return state; } }
Lưu ý bạn không sử dụng bộ giảm để sắp xếp dữ liệu. Sắp xếp có thể được coi là mối quan tâm của chế độ xem vì chế độ xem hiển thị thông tin cho user . Bạn có thể có một chế độ xem sắp xếp theo tên và một chế độ xem sắp xếp theo số lượt xem, vì vậy tốt hơn nên để các thành phần riêng lẻ xử lý việc sắp xếp. Thay vào đó, hãy giữ cho bộ giảm tập trung vào việc cập nhật dữ liệu và thành phần tập trung vào việc chuyển đổi dữ liệu sang chế độ xem có thể sử dụng cho user .
Công cụ giảm thiểu này cũng không hoàn hảo vì bạn có thể thêm các loài chim có cùng tên. Trong ứng dụng production , bạn cần phải xác thực trước khi thêm hoặc cấp cho chim một id
duy nhất để bạn có thể chọn chim theo id
thay vì name
.
Đến đây bạn có hai hành động hoàn chỉnh và một bộ giảm. Bước cuối cùng là xuất bộ giảm tốc để nó có thể khởi tạo cửa hàng. Trong bước đầu tiên, bạn đã tạo cửa hàng bằng cách truyền một hàm trả về một đối tượng. Bạn sẽ làm điều tương tự trong trường hợp này. Hàm sẽ lấy store
và action
, sau đó chuyển phần cụ thể của store
cho các bộ giảm cùng với hành động. Nó sẽ trông giống như thế này:
export function birdApp(store={}, action) { return { birds: birds(store.birds, action) } }
Để đơn giản hóa mọi thứ, Redux có một chức năng trợ giúp được gọi là combineReducers
kết hợp các bộ giảm cho bạn.
Inside of birds.js
, nhập các đầu combineReducers
từ redux
. Sau đó gọi hàm với birds
và xuất kết quả:
import { combineReducers } from 'redux'; const ADD_BIRD = 'ADD_BIRD'; const INCREMENT_BIRD = 'INCREMENT_BIRD'; export function addBird(bird) { return { type: ADD_BIRD, bird, } } export function incrementBird(bird) { return { type: INCREMENT_BIRD, bird } } const defaultBirds = [ { name: 'robin', views: 1, } ]; function birds(state=defaultBirds, action) { switch (action.type) { case ADD_BIRD: return [ ...state, { name: action.bird, views: 1 } ]; case INCREMENT_BIRD: const bird = state.find(b => action.bird === b.name); const birds = state.filter(b => action.bird !== b.name); return [ ...birds, { ...bird, views: bird.views + 1 } ]; default: return state; } } const birdApp = combineReducers({ birds }); export default birdApp;
Lưu và đóng file .
Tất cả các hành động và bộ giảm của bạn đã được cài đặt . Bước cuối cùng là khởi tạo cửa hàng của bạn bằng cách sử dụng các bộ giảm kết hợp thay vì hàm giữ chỗ.
Mở src/index.js
:
- nano src/index.js
Nhập birdApp
từ birds.js
Sau đó khởi tạo store
bằng birdApp
:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './components/App/App'; import * as serviceWorker from './serviceWorker'; import { Provider } from 'react-redux' import { createStore } from 'redux' import birdApp from './store/birds/birds'; const store = createStore(birdApp); ReactDOM.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.getElementById('root') ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
Lưu và đóng file . Khi bạn thực hiện, trình duyệt sẽ làm mới ứng dụng của bạn:
Trong bước này, bạn đã tạo các hành động và bộ giảm. Bạn đã học cách tạo các hành động trả về một type
và cách tạo các bộ giảm sử dụng hành động để tạo và trả về một trạng thái mới dựa trên hành động. Cuối cùng, bạn kết hợp các bộ giảm bớt thành một hàm mà bạn đã sử dụng để khởi tạo cửa hàng.
Cửa hàng Redux của bạn hiện đã được cài đặt xong và sẵn sàng cho các thay đổi. Trong bước tiếp theo, bạn sẽ gửi các hành động từ một thành phần để cập nhật dữ liệu.
Bước 3 - Điều phối các thay đổi trong một thành phần
Trong bước này, bạn sẽ nhập và gọi các hành động của bạn từ thành phần của bạn . Bạn sẽ sử dụng một phương pháp được gọi là dispatch
để gửi hành động và bạn sẽ gửi các hành động bên trong trình xử lý sự kiện cho form
và button
.
Đến cuối bước này, bạn sẽ có một ứng dụng hoạt động kết hợp cửa hàng Redux và các thành phần tùy chỉnh của bạn. Bạn có thể cập nhật cửa hàng Redux trong thời gian thực và có thể hiển thị thông tin trong thành phần của bạn khi nó thay đổi.
Đến đây bạn có các hành động đang làm việc, bạn cần kết nối chúng với các sự kiện của bạn để có thể cập nhật cửa hàng. Phương thức bạn sẽ sử dụng được gọi là công dispatch
và nó sẽ gửi một hành động cụ thể đến cửa hàng Redux. Khi Redux nhận được một hành động mà bạn đã gửi đi, nó sẽ chuyển hành động đó cho các bộ giảm và chúng sẽ cập nhật dữ liệu.
Mở App.js
:
- nano src/components/App/App.js
Inside of App.js
nhập Hook useDispath
từ useDispath
react-redux
. Sau đó, gọi hàm để tạo một hàm dispatch
mới:
import React from 'react'; import { useDispatch, useSelector } from 'react-redux' import './App.css'; function App() { ... } export default App;
Tiếp theo, bạn cần nhập các hành động của bạn . Lưu ý , các hành động là các hàm trả về một đối tượng. Đối tượng là thứ cuối cùng bạn sẽ chuyển vào hàm dispatch
.
Nhập incrementBird
từ cửa hàng. Sau đó, tạo sự kiện onClick
trên nút. Khi user nhấp vào nút, hãy gọi incrementBird
bằng bird.name
và chuyển kết quả đến công dispatch
. Để làm cho mọi thứ dễ đọc hơn, hãy gọi hàm incrementBird
bên trong công dispatch
:
import React from 'react'; import { useDispatch, useSelector } from 'react-redux' import { incrementBird } from '../../store/birds/birds'; import './App.css'; function App() { const birds = useSelector(state => state.birds); const dispatch = useDispatch(); return ( <div className="wrapper"> <h1>Bird List</h1> <form> <label> <p> Add Bird </p> <input type="text" /> </label> <div> <button type="submit">Add</button> </div> </form> <ul> {birds.map(bird => ( <li key={bird.name}> <h3>{bird.name}</h3> <div> Views: {bird.views} <button onClick={() => dispatch(incrementBird(bird.name))}><span role="img" aria-label="add">➕</span></button> </div> </li> ))} </ul> </div> ); } export default App;
Lưu các file . Khi làm như vậy, bạn có thể tăng số lượng robin:
Tiếp theo, bạn cần gửi hành động addBird
. Quá trình này sẽ thực hiện hai bước: lưu dữ liệu đầu vào vào trạng thái nội bộ và kích hoạt gửi đi với onSubmit
.
Sử dụng useState
Hook để lưu giá trị đầu vào. Đảm bảo chuyển đổi đầu vào thành một thành phần được kiểm soát bằng cách đặt value
trên đầu vào. Hãy xem hướng dẫn Cách tạo biểu mẫu trong React để có cái nhìn chuyên sâu hơn về các thành phần được kiểm soát.
áp dụng các thay đổi sau đối với mã của bạn:
import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux' import { incrementBird } from '../../store/birds/birds'; import './App.css'; function App() { const [birdName, setBird] = useState(''); const birds = useSelector(state => state.birds); const dispatch = useDispatch(); return ( <div className="wrapper"> <h1>Bird List</h1> <form> <label> <p> Add Bird </p> <input type="text" onChange={e => setBird(e.target.value)} value={birdName} /> </label> <div> <button type="submit">Add</button> </div> </form> <ul> ... </ul> </div> ); } export default App;
Tiếp theo, nhập addBird
từ birds.js
, sau đó tạo một hàm có tên là handleSubmit
. Bên trong hàm handleSubmit
, ngăn việc gửi biểu mẫu trang bằng event.preventDefault
, sau đó gửi hành động addBird
với birdName
làm đối số. Sau khi thực hiện hành động, hãy gọi setBird('')
để xóa đầu vào. Cuối cùng, chuyển handleSubmit
đến trình xử lý sự kiện onSubmit
trên form
:
import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux' import { addBird, incrementBird } from '../../store/birds/birds'; import './App.css'; function App() { const [birdName, setBird] = useState(''); const birds = useSelector(state => state.birds); const dispatch = useDispatch(); const handleSubmit = event => { event.preventDefault(); dispatch(addBird(birdName)) setBird(''); }; return ( <div className="wrapper"> <h1>Bird List</h1> <form onSubmit={handleSubmit}> <label> <p> Add Bird </p> <input type="text" onChange={e => setBird(e.target.value)} value={birdName} /> </label> <div> <button type="submit">Add</button> </div> </form> <ul> {birds.map(bird => ( <li key={bird.name}> <h3>{bird.name}</h3> <div> Views: {bird.views} <button onClick={() => dispatch(incrementBird(bird.name))}><span role="img" aria-label="add">➕</span></button> </div> </li> ))} </ul> </div> ); } export default App;
Lưu các file . Khi bạn làm như vậy, trình duyệt sẽ reload và bạn có thể thêm một con chim:
Đến đây bạn đang gọi các hành động của bạn và cập nhật danh sách các loài chim của bạn trong cửa hàng. Lưu ý khi ứng dụng của bạn được làm mới, bạn sẽ mất thông tin trước đó. Kho lưu trữ tất cả được chứa trong bộ nhớ và do đó, việc làm mới trang sẽ xóa sạch dữ liệu.
Thứ tự danh sách này cũng sẽ thay đổi nếu bạn tăng một con chim cao hơn trong danh sách.
Như bạn đã thấy ở Bước 2, trình giảm thiểu của bạn không liên quan đến việc sắp xếp dữ liệu. Để ngăn sự thay đổi không mong muốn trong các thành phần, bạn có thể sắp xếp dữ liệu trong thành phần của bạn . Thêm một hàm sort()
vào mảng birds
. Lưu ý sắp xếp sẽ thay đổi mảng và bạn không bao giờ muốn thay đổi cửa hàng. Đảm bảo tạo một mảng mới bằng cách trải rộng dữ liệu trước khi sắp xếp:
import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux' import { addBird, incrementBird } from '../../store/birds/birds'; import './App.css'; function App() { const [birdName, setBird] = useState(''); const birds = [...useSelector(state => state.birds)].sort((a, b) => { return a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1; }); const dispatch = useDispatch(); const handleSubmit = event => { event.preventDefault(); dispatch(addBird(birdName)) setBird(''); }; return ( <div className="wrapper"> <h1>Bird List</h1> <form onSubmit={handleSubmit}> <label> <p> Add Bird </p> <input type="text" onChange={e => setBird(e.target.value)} value={birdName} /> </label> <div> <button type="submit">Add</button> </div> </form> <ul> {birds.map(bird => ( <li key={bird.name}> <h3>{bird.name}</h3> <div> Views: {bird.views} <button onClick={() => dispatch(incrementBird(bird.name))}><span role="img" aria-label="add">➕</span></button> </div> </li> ))} </ul> </div> ); } export default App;
Lưu các file . Khi bạn làm như vậy, các thành phần sẽ giữ nguyên theo thứ tự bảng chữ cái khi bạn tăng dần các con chim.
Điều quan trọng là không thử và làm quá nhiều trong cửa hàng Redux của bạn. Giữ các bộ giảm tập trung vào việc duy trì thông tin cập nhật, sau đó kéo và thao tác dữ liệu cho user của bạn bên trong thành phần.
Lưu ý: Trong hướng dẫn này, hãy lưu ý có một lượng mã hợp lý cho mỗi hành động và trình giảm thiểu. May mắn là có một dự án được hỗ trợ chính thức gọi là Bộ công cụ Redux có thể giúp bạn giảm số lượng mã soạn sẵn. Bộ công cụ Redux cung cấp một tập hợp các tiện ích phù hợp để nhanh chóng tạo các hành động và trình giảm bớt, đồng thời cho phép bạn tạo và cấu hình cửa hàng của bạn với ít mã hơn.
Trong bước này, bạn đã gửi các hành động của bạn từ một thành phần. Bạn đã học cách gọi các hành động và cách gửi kết quả đến một hàm điều phối và bạn đã kết nối chúng với các trình xử lý sự kiện trên các thành phần của bạn để tạo ra một cửa hàng tương tác đầy đủ. Cuối cùng, bạn đã học được cách duy trì trải nghiệm user nhất quán bằng cách sắp xếp dữ liệu mà không trực tiếp thay đổi cửa hàng.
Kết luận
Redux là một cửa hàng đơn lẻ phổ biến. Nó có thể thuận lợi khi làm việc với các thành phần cần một nguồn thông tin chung. Tuy nhiên, không phải lúc nào nó cũng là sự lựa chọn đúng đắn trong mọi công trình. Các dự án nhỏ hơn hoặc các dự án có các thành phần biệt lập sẽ có thể sử dụng ngữ cảnh và quản lý nhà nước được tích hợp sẵn. Nhưng khi các ứng dụng của bạn ngày càng phức tạp, bạn có thể thấy rằng bộ nhớ trung tâm rất quan trọng để duy trì tính toàn vẹn của dữ liệu. Trong những trường hợp như vậy, Redux là một công cụ tuyệt vời để tạo một repodata thống nhất duy nhất mà bạn có thể sử dụng trên các thành phần của bạn với nỗ lực tối thiểu.
Nếu bạn muốn đọc thêm các hướng dẫn về React, hãy xem trang Chủ đề React của ta hoặc quay lại trang Cách viết mã trong chuỗi React.js .
Các tin liên quan