Cách gọi API web với useEffect Hook trong React
Trong phát triển React , giao diện lập trình ứng dụng web (API) là một phần không thể thiếu trong các thiết kế ứng dụng một trang (SPA) . API là cách chính để các ứng dụng giao tiếp theo chương trình với server để cung cấp cho user dữ liệu thời gian thực và lưu các thay đổi của user . Trong các ứng dụng React, bạn sẽ sử dụng các API để tải các tùy chọn của user , hiển thị thông tin user , tìm nạp cấu hình hoặc thông tin bảo mật và lưu các thay đổi trạng thái ứng dụng. Trong hướng dẫn này, bạn sẽ sử dụng useEffect
và useState
Hooks để tìm nạp và hiển thị thông tin trong ứng dụng mẫu, sử dụng server JSON làm API local cho mục đích thử nghiệm. Bạn sẽ tải thông tin khi một thành phần được mount lần đầu tiên và lưu dữ liệu đầu vào của khách hàng bằng một API. Bạn cũng sẽ làm mới dữ liệu khi user thực hiện thay đổi và học cách bỏ qua các yêu cầu API khi một thành phần ngắt kết nối. Đến cuối hướng dẫn này, bạn có thể kết nối các ứng dụng React của bạn với nhiều API khác nhau và bạn có thể gửi và nhận dữ liệu thời gian thực.
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
api-tutorial
làm tên dự án.Bạn sẽ sử dụng các thành phần React và Hook trong hướng dẫn này, bao gồm
useState
vàuseEffect
Hooks. 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 Hooks trên Thành phần React và Cách Xử lý Tải dữ liệu không đồng bộ, Tải lười biếng và Tách mã với React .Bạn cũng cần kiến thức cơ bản về JavaScript và HTML, 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 và trong Cách viết mã trong JavaScript . Kiến thức cơ bản về CSS cũng sẽ hữu ích, bạn có thể tìm thấy kiến thức này tại Mạng nhà phát triển Mozilla .
Bước 1 - Tạo dự án và API local
Trong bước này, bạn sẽ tạo API REST local bằng server JSON , server này sẽ sử dụng làm nguồn dữ liệu thử nghiệm. Sau đó, bạn sẽ xây dựng một ứng dụng để hiển thị danh sách hàng tạp hóa và thêm các mặt hàng vào danh sách. Server JSON sẽ là API local của bạn và sẽ cung cấp cho bạn một URL trực tiếp để thực hiện các yêu cầu GET
và POST
. Với API local , bạn có cơ hội tạo mẫu và thử nghiệm các thành phần trong khi bạn hoặc một group khác phát triển các API trực tiếp.
Đến cuối bước này, bạn có thể tạo các API giả local mà bạn có thể kết nối với các ứng dụng React của bạn .
Trên nhiều group nhanh nhẹn , group giao diện user và API làm việc song song với một vấn đề. Để phát triển ứng dụng giao diện user trong khi API từ xa vẫn đang được phát triển, bạn có thể tạo version local mà bạn có thể sử dụng trong khi đợi API từ xa hoàn chỉnh.
Có nhiều cách để tạo một API local giả. Bạn có thể tạo một server đơn giản bằng Node hoặc một ngôn ngữ khác, nhưng cách nhanh nhất là sử dụng gói JSON server Node. Dự án này tạo một API REST local từ tệp JSON .
Để bắt đầu, hãy cài đặt json-server
:
- npm install --save-dev json-server
Khi quá trình cài đặt hoàn tất, bạn sẽ nhận được thông báo thành công:
Output+ json-server@0.16.1 added 108 packages from 40 contributors and audited 1723 packages in 14.505s 73 packages are looking for funding run `npm fund` for details found 0 vulnerabilities
json-server
tạo một API dựa trên một đối tượng JavaScript . Các khóa là các đường dẫn URL và các giá trị được trả về dưới dạng phản hồi. Bạn lưu trữ local đối tượng JavaScript và commit nó với quyền kiểm soát nguồn của bạn.
Mở một file có tên db.json
trong folder root của ứng dụng của bạn. Đây sẽ là JSON lưu trữ thông tin bạn yêu cầu từ API:
- nano db.json
Thêm một đối tượng với khóa của list
và một mảng giá trị với một id
và một khóa của item
. Điều này sẽ liệt kê các mặt hàng cho danh sách tạp hóa. list
khóa cuối cùng sẽ cung cấp cho bạn một URL có điểm cuối là /list
:
{ "list": [ { "id": 1, "item": "bread" }, { "id": 2, "item": "grapes" } ] }
Trong đoạn mã này, bạn có bread
và grapes
được mã hóa cứng làm điểm bắt đầu cho danh sách hàng tạp hóa của bạn .
Lưu và đóng file . Để chạy server API, bạn sẽ sử dụng json-server
từ dòng lệnh với một điểm đối số đến file cấu hình API. Thêm nó dưới dạng một tập lệnh trong package.json
của bạn.
Mở package.json
:
- nano package.json
Sau đó, thêm một tập lệnh để chạy API. Ngoài ra, hãy thêm một thuộc tính delay
. Điều này sẽ làm giảm phản hồi, tạo ra độ trễ giữa yêu cầu API của bạn và phản hồi API. Điều này sẽ cung cấp cho bạn một số thông tin chi tiết về cách ứng dụng sẽ hoạt động khi chờ phản hồi của server . Thêm delay
1500
mili giây. Cuối cùng, chạy API trên cổng 3333
bằng cách sử dụng tùy chọn -p
để nó không xung đột với tập lệnh chạy create-react-app
:
{ "name": "do-14-api", "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-scripts": "3.4.3" }, "scripts": { "api": "json-server db.json -p 3333 --delay 1500", "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": { "json-server": "^0.16.1" } }
Lưu và đóng file . Trong một terminal hoặc tab mới, hãy khởi động server API bằng lệnh sau:
- npm run api
Tiếp tục chạy phần này trong phần còn lại của hướng dẫn.
Khi bạn chạy lệnh, bạn sẽ nhận được kết quả liệt kê các tài nguyên API:
Output> json-server db.json -p 3333 \{^_^}/ hi! Loading db.json Done Resources http://localhost:3333/list Home http://localhost:3333 Type s + enter at any time to create a snapshot of the database
Mở http://localhost:3333/list
và bạn sẽ tìm thấy API trực tiếp:
Khi bạn mở một điểm cuối trong trình duyệt của bạn , bạn đang sử dụng phương thức GET
. Nhưng json-server
không giới hạn ở phương thức GET
. Bạn cũng có thể thực hiện nhiều phương pháp REST khác. Ví dụ: bạn có thể POST
các mục mới. Trong tab hoặc cửa sổ terminal mới, sử dụng curl
để POST
một mục mới với loại application/json
:
- curl -d '{"item":"rice"}' -H 'Content-Type: application/json' -X POST http://localhost:3333/list
Lưu ý bạn phải xâu chuỗi nội dung trước khi gửi. Sau khi chạy lệnh curl
, bạn sẽ nhận được thông báo thành công:
Output{ "item": "rice", "id": 3 }
Nếu bạn làm mới trình duyệt, mục mới sẽ xuất hiện:
Yêu cầu POST
cũng sẽ cập nhật file db.json
. Hãy lưu ý đến những thay đổi, vì không có rào cản nào đối với việc vô tình lưu nội dung không có cấu trúc hoặc không hữu ích khi bạn làm việc trên ứng dụng của bạn . Đảm bảo kiểm tra bất kỳ thay đổi nào trước khi chuyển sang kiểm soát version .
Trong bước này, bạn đã tạo một API local . Bạn đã học cách tạo file tĩnh với các giá trị mặc định và cách tìm nạp hoặc cập nhật các giá trị đó bằng cách sử dụng các hành động RESTful như GET
và POST
. Trong bước tiếp theo, bạn sẽ tạo các dịch vụ để tìm nạp dữ liệu từ API và hiển thị trong ứng dụng của bạn .
Bước 2 - Tìm nạp dữ liệu từ API với useEffect
Trong bước này, bạn sẽ tìm nạp một danh sách các cửa hàng tạp hóa bằng cách sử dụng useEffect
Hook. Bạn sẽ tạo một dịch vụ để sử dụng các API trong các folder riêng biệt và gọi dịch vụ đó trong các thành phần React của bạn . Sau khi bạn gọi dịch vụ, bạn sẽ lưu dữ liệu bằng useState
Hook và hiển thị kết quả trong thành phần của bạn.
Đến cuối bước này, bạn có thể gọi các API web bằng phương thức Tìm nạp và useEffect
Hook. Bạn cũng sẽ có thể lưu và hiển thị kết quả.
Đến đây bạn đã có một API hoạt động, bạn cần một dịch vụ để tìm nạp dữ liệu và các thành phần để hiển thị thông tin. Bắt đầu bằng cách tạo một dịch vụ. Bạn có thể tìm nạp dữ liệu trực tiếp bên trong bất kỳ thành phần React nào, nhưng các dự án của bạn sẽ dễ dàng duyệt và cập nhật hơn nếu bạn giữ các chức năng truy xuất dữ liệu tách biệt với các thành phần hiển thị của bạn . Điều này sẽ cho phép bạn sử dụng lại các phương thức trên các thành phần, mô phỏng trong các bài kiểm tra và cập nhật URL khi điểm cuối thay đổi.
Tạo một folder được gọi là services
bên trong folder src
:
- mkdir src/services
Sau đó, mở một file có tên list.js
trong editor của bạn:
- nano src/services/list.js
Bạn sẽ sử dụng file này cho bất kỳ hành động nào trên điểm cuối /list
. Thêm một hàm để truy xuất dữ liệu bằng cách sử dụng hàm fetch
:
export function getList() { return fetch('http://localhost:3333/list') .then(data => data.json()) }
Mục tiêu duy nhất của hàm này là truy cập dữ liệu và chuyển đổi phản hồi thành JSON bằng phương thức data.json()
. GET
là hành động mặc định, vì vậy bạn không cần bất kỳ tham số nào khác.
Ngoài fetch
, có những thư viện phổ biến khác như Axios có thể cung cấp cho bạn giao diện trực quan và sẽ cho phép bạn thêm tiêu đề mặc định hoặc thực hiện các hành động khác trên dịch vụ. Nhưng fetch
sẽ hoạt động cho hầu hết các yêu cầu.
Lưu và đóng file . Tiếp theo, mở App.css
và thêm một số kiểu dáng tối thiểu:
- nano src/components/App/App.css
Thêm một lớp wrapper
với một lượng nhỏ đệm bằng cách thay thế CSS bằng như sau:
.wrapper { padding: 15px; }
Lưu và đóng file . Đến đây bạn cần thêm mã để truy xuất dữ liệu và hiển thị nó trong ứng dụng của bạn .
Mở App.js
:
- nano src/components/App/App.js
Trong các thành phần chức năng, bạn sử dụng useEffect
Hook để tìm nạp dữ liệu khi thành phần tải hoặc một số thông tin thay đổi. Để biết thêm thông tin về việc sử useEffect
Hook, hãy xem Cách xử lý khi tải dữ liệu không đồng bộ, tải useEffect
và tách mã bằng React . Bạn cũng cần lưu kết quả với useState
Hook.
Nhập useEffect
và useState
, sau đó tạo một biến có tên là list
và một setter được gọi là setList
để giữ dữ liệu bạn tìm nạp từ dịch vụ bằng useState
Hook:
import React, { useEffect, useState } from 'react'; import './App.css'; function App() { const [list, setList] = useState([]); return( <> </> ) } export default App;
Tiếp theo, nhập dịch vụ, sau đó gọi dịch vụ bên trong useEffect
Hook của bạn. Cập nhật list
với setList
nếu thành phần được mount . Để hiểu lý do tại sao bạn nên kiểm tra xem thành phần có được mount hay không trước khi cài đặt dữ liệu, hãy xem Bước 2 - Ngăn ngừa lỗi trên các thành phần chưa được mount trong Cách xử lý tính năng tải dữ liệu không đồng bộ, tải chậm và phân tách mã bằng React .
Hiện tại, bạn chỉ chạy hiệu ứng một lần khi tải trang, vì vậy mảng phụ thuộc sẽ trống. Trong bước tiếp theo, bạn sẽ kích hoạt hiệu ứng dựa trên các hành động trên trang khác nhau đảm bảo rằng bạn luôn có thông tin cập nhật nhất.
Thêm mã được đánh dấu sau:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList } from '../../services/list'; function App() { const [list, setList] = useState([]); useEffect(() => { let mounted = true; getList() .then(items => { if(mounted) { setList(items) } }) return () => mounted = false; }, []) return( <> </> ) } export default App;
Cuối cùng, lặp lại các mục bằng .map
và hiển thị chúng trong danh sách:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList } from '../../services/list'; function App() { const [list, setList] = useState([]); useEffect(() => { let mounted = true; getList() .then(items => { if(mounted) { setList(items) } }) return () => mounted = false; }, []) return( <div className="wrapper"> <h1>My Grocery List</h1> <ul> {list.map(item => <li key={item.item}>{item.item}</li>)} </ul> </div> ) } export default App;
Lưu và đóng file . Khi bạn làm như vậy, trình duyệt sẽ làm mới và bạn sẽ tìm thấy danh sách các mục:
Trong bước này, bạn cài đặt một dịch vụ để lấy dữ liệu từ một API. Bạn đã học cách gọi dịch vụ bằng useEffect
Hook và cách đặt dữ liệu trên trang. Bạn cũng hiển thị dữ liệu bên trong JSX của bạn .
Trong bước tiếp theo, bạn sẽ gửi dữ liệu tới API bằng cách sử dụng POST
và sử dụng phản hồi để thông báo cho user của bạn rằng một hành động đã thành công.
Bước 3 - Gửi dữ liệu tới API
Trong bước này, bạn sẽ gửi dữ liệu trở lại API bằng cách sử dụng API Tìm nạp và phương thức POST
. Bạn sẽ tạo một thành phần sẽ sử dụng biểu mẫu web để gửi dữ liệu với trình xử lý sự kiện onSubmit
và sẽ hiển thị thông báo thành công khi hành động hoàn tất.
Khi kết thúc bước này, bạn có thể gửi thông tin tới API và bạn có thể thông báo cho user khi yêu cầu được giải quyết.
Gửi dữ liệu đến một dịch vụ
Bạn có một ứng dụng sẽ hiển thị danh sách các mặt hàng tạp hóa, nhưng nó không phải là một ứng dụng tạp hóa rất hữu ích trừ khi bạn cũng có thể lưu nội dung. Bạn cần tạo một dịch vụ sẽ POST
một mục mới lên API.
Mở src/services/list.js
:
- nano src/services/list.js
Bên trong file , hãy thêm một hàm sẽ lấy một item
làm đối số và sẽ gửi dữ liệu bằng phương thức POST
tới API. Như trước đây, bạn có thể sử dụng API Tìm nạp. Lần này, bạn cần thêm thông tin. Thêm một đối tượng tùy chọn làm đối số thứ hai. Bao gồm phương thức— POST
— cùng với tiêu đề để đặt Content-Type
thành application/json
. Cuối cùng, gửi đối tượng mới trong body
. Đảm bảo chuyển đổi đối tượng thành chuỗi bằng JSON.stringify
.
Khi bạn nhận được phản hồi, hãy chuyển đổi giá trị thành JSON:
export function getList() { return fetch('http://localhost:3333/list') .then(data => data.json()) } export function setItem(item) { return fetch('http://localhost:3333/list', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ item }) }) .then(data => data.json()) }
Lưu và đóng file .
Lưu ý: Trong các ứng dụng production , bạn cần thêm xử lý và kiểm tra lỗi. Ví dụ: nếu bạn viết sai chính tả cho điểm cuối, bạn vẫn nhận được phản hồi 404
và phương thức data.json()
sẽ trả về một đối tượng trống. Để giải quyết vấn đề, thay vì chuyển đổi phản hồi thành JSON, bạn có thể kiểm tra thuộc tính data.ok
Nếu nó là sai, bạn có thể tạo ra một lỗi và sau đó sử dụng phương thức .catch
trong thành phần của bạn để hiển thị thông báo lỗi cho user .
Đến đây bạn đã tạo một dịch vụ, bạn cần sử dụng dịch vụ bên trong thành phần của bạn .
Mở App.js
:
- nano src/components/App/App.js
Thêm phần tử form
xung quanh input
và button
gửi:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList } from '../../services/list'; function App() { const [list, setList] = useState([]); useEffect(() => { let mounted = true; getList() .then(items => { if(mounted) { setList(items) } }) return () => mounted = false; }, []) return( <div className="wrapper"> <h1>My Grocery List</h1> <ul> {list.map(item => <li key={item.item}>{item.item}</li>)} </ul> <form> <label> <p>New Item</p> <input type="text" /> </label> <button type="submit">Submit</button> </form> </div> ) } export default App;
Đảm bảo bao quanh input
bằng một label
để biểu mẫu có thể truy cập được bằng trình đọc màn hình. Một phương pháp hay là thêm type="submit"
vào button
để rõ ràng role của việc gửi biểu mẫu.
Lưu các file . Khi bạn làm như vậy, trình duyệt sẽ làm mới và bạn sẽ tìm thấy biểu mẫu của bạn .
Tiếp theo, chuyển đổi input
thành một thành phần được kiểm soát . Bạn cần một thành phần được kiểm soát để có thể xóa trường sau khi user gửi thành công một mục danh sách mới.
Trước tiên, hãy tạo một trình xử lý trạng thái mới để giữ và đặt thông tin đầu vào bằng useState
Hook:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList } from '../../services/list'; function App() { const [itemInput, setItemInput] = useState(''); const [list, setList] = useState([]); useEffect(() => { let mounted = true; getList() .then(items => { if(mounted) { setList(items) } }) return () => mounted = false; }, []) return( <div className="wrapper"> <h1>My Grocery List</h1> <ul> {list.map(item => <li key={item.item}>{item.item}</li>)} </ul> <form> <label> <p>New Item</p> <input type="text" onChange={event => setItemInput(event.target.value)} value={itemInput} /> </label> <button type="submit">Submit</button> </form> </div> ) } export default App;
Sau khi tạo các trình xử lý trạng thái, hãy đặt giá trị của input
thành itemInput
và cập nhật giá trị bằng cách chuyển event.target.value
đến hàm setItemInput
bằng trình xử lý sự kiện onChange
.
Như vậy, user của bạn có thể điền vào biểu mẫu với các mục danh sách mới. Tiếp theo, bạn sẽ kết nối biểu mẫu với dịch vụ của bạn .
Tạo một hàm có tên là handleSubmit
. handleSubmit
sẽ lấy một sự kiện làm đối số và sẽ gọi event.preventDefault()
để ngăn biểu mẫu làm mới trình duyệt.
Nhập setItem
từ dịch vụ, sau đó gọi setItem
với giá trị itemInput
bên trong hàm handleSubmit
. Kết nối handleSubmit
với biểu mẫu bằng cách chuyển nó tới trình xử lý sự kiện onSubmit
:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList, setItem } from '../../services/list'; function App() { const [itemInput, setItemInput] = useState(''); const [list, setList] = useState([]); useEffect(() => { let mounted = true; getList() .then(items => { if(mounted) { setList(items) } }) return () => mounted = false; }, []) const handleSubmit = (e) => { e.preventDefault(); setItem(itemInput) }; return( <div className="wrapper"> <h1>My Grocery List</h1> <ul> {list.map(item => <li key={item.item}>{item.item}</li>)} </ul> <form onSubmit={handleSubmit}> <label> <p>New Item</p> <input type="text" onChange={event => setItemInput(event.target.value)} value={itemInput} /> </label> <button type="submit">Submit</button> </form> </div> ) } export default App;
Lưu các file . Khi bạn làm như vậy, bạn có thể gửi các giá trị. Lưu ý bạn sẽ nhận được phản hồi thành công trong tab mạng. Nhưng danh sách không cập nhật và đầu vào không rõ ràng.
Hiển thị Thông báo Thành công
Luôn luôn là một phương pháp hay để cung cấp cho user một số dấu hiệu cho thấy hành động của họ đã thành công. Nếu không, user có thể thử và gửi lại một giá trị nhiều lần hoặc có thể nghĩ rằng hành động của họ không thành công và sẽ rời khỏi ứng dụng.
Để thực hiện việc này, hãy tạo một biến trạng thái và hàm setter với useState
để cho biết có hiển thị cho user một thông báo cảnh báo hay không. Nếu alert
là đúng, hãy hiển thị <h2>
với thông báo Gửi thành công .
Khi lời hứa setItem
giải quyết, hãy xóa đầu vào và đặt thông báo cảnh báo:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList, setItem } from '../../services/list'; function App() { const [alert, setAlert] = useState(false); const [itemInput, setItemInput] = useState(''); const [list, setList] = useState([]); useEffect(() => { let mounted = true; getList() .then(items => { if(mounted) { setList(items) } }) return () => mounted = false; }, []) const handleSubmit = (e) => { e.preventDefault(); setItem(itemInput) .then(() => { setItemInput(''); setAlert(true); }) }; return( <div className="wrapper"> <h1>My Grocery List</h1> <ul> {list.map(item => <li key={item.item}>{item.item}</li>)} </ul> {alert && <h2> Submit Successful</h2>} <form onSubmit={handleSubmit}> <label> <p>New Item</p> <input type="text" onChange={event => setItemInput(event.target.value)} value={itemInput} /> </label> <button type="submit">Submit</button> </form> </div> ) } export default App;
Lưu các file . Khi bạn làm như vậy, trang sẽ làm mới và bạn sẽ thấy thông báo thành công sau khi yêu cầu API được giải quyết.
Có nhiều cách tối ưu hóa khác mà bạn có thể thêm vào. Ví dụ: bạn có thể cần vô hiệu hóa đầu vào biểu mẫu khi có một yêu cầu đang hoạt động. Bạn có thể tìm hiểu thêm về cách tắt các phần tử biểu mẫu trong Cách tạo biểu mẫu trong React .
Đến đây bạn đã cảnh báo user rằng kết quả đã thành công, nhưng thông báo cảnh báo không biến mất và danh sách không cập nhật. Để khắc phục điều này, hãy bắt đầu bằng cách ẩn cảnh báo. Trong trường hợp này, bạn muốn ẩn thông tin sau một khoảng thời gian ngắn, chẳng hạn như một giây. Bạn có thể sử dụng hàm setTimeout
để gọi setAlert(false)
, nhưng bạn cần bao bọc nó trong useEffect
đảm bảo rằng nó không chạy trên mọi kết xuất thành phần.
Inside of App.js
tạo ra một hiệu ứng mới và chuyển alert
đến một loạt các trình kích hoạt. Điều này sẽ làm cho hiệu ứng chạy bất kỳ thay đổi alert
nào. Lưu ý điều này sẽ chạy nếu alert
thay đổi từ false
thành true
, nhưng nó cũng sẽ chạy khi alert
thay đổi từ true
thành false
. Vì bạn chỉ muốn ẩn cảnh báo nếu nó được hiển thị, hãy thêm điều kiện bên trong hiệu ứng để chỉ chạy setTimeout
nếu alert
là true
:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList, setItem } from '../../services/list'; function App() { const [alert, setAlert] = useState(false); const [itemInput, setItemInput] = useState(''); const [list, setList] = useState([]); ... useEffect(() => { if(alert) { setTimeout(() => { setAlert(false); }, 1000) } }, [alert]) const handleSubmit = (e) => { e.preventDefault(); setItem(itemInput) .then(() => { setItemInput(''); setAlert(true); }) }; return( <div className="wrapper"> ... </div> ) } export default App;
Chạy hàm setTimeout
sau 1000
mili giây đảm bảo user có thời gian đọc thay đổi.
Lưu các file . Đến đây bạn có một hiệu ứng sẽ chạy khi nào alert
thay đổi. Nếu có một cảnh báo hoạt động, nó sẽ bắt đầu chức năng thời gian chờ sẽ đóng cảnh báo sau một giây.
Làm mới dữ liệu đã tìm nạp
Đến đây bạn cần một cách để làm mới danh sách dữ liệu cũ. Để thực hiện việc này, bạn có thể thêm trình kích hoạt mới vào useEffect
Hook để chạy lại yêu cầu getList
. Để đảm bảo bạn có dữ liệu phù hợp nhất, bạn cần một trình kích hoạt sẽ cập nhật bất kỳ lúc nào có thay đổi đối với dữ liệu từ xa. May mắn là bạn có thể sử dụng lại trạng thái alert
để kích hoạt một lần làm mới dữ liệu khác vì bạn biết rằng nó sẽ chạy khi nào user cập nhật dữ liệu. Như trước đây, bạn phải lập kế hoạch cho thực tế là hiệu ứng sẽ chạy mỗi khi alert
thay đổi kể cả khi thông báo cảnh báo biến mất.
Lần này, hiệu ứng cũng cần tìm nạp dữ liệu khi tải trang. Tạo một điều kiện sẽ thoát khỏi hàm trước khi tìm nạp dữ liệu nếu list.length
là true — cho biết bạn đã tìm nạp dữ liệu — và alert
là false
biết bạn đã làm mới dữ liệu. Đảm bảo thêm alert
và list
vào mảng trình kích hoạt:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList, setItem } from '../../services/list'; function App() { const [alert, setAlert] = useState(false); const [itemInput, setItemInput] = useState(''); const [list, setList] = useState([]); useEffect(() => { let mounted = true; if(list.length && !alert) { return; } getList() .then(items => { if(mounted) { setList(items) } }) return () => mounted = false; }, [alert, list]) ... return( <div className="wrapper"> ... </div> ) } export default App;
Lưu các file . Khi bạn làm như vậy, dữ liệu sẽ làm mới sau khi bạn gửi một mặt hàng mới:
Trong trường hợp này, alert
không liên quan trực tiếp đến trạng thái list
. Tuy nhiên, nó xảy ra cùng lúc với một sự kiện sẽ làm mất hiệu lực của dữ liệu cũ, vì vậy bạn có thể sử dụng nó để làm mới dữ liệu.
Ngăn cập nhật trên các thành phần chưa được mount
Vấn đề cuối cùng bạn cần tính đến là đảm bảo bạn không đặt trạng thái trên một thành phần chưa được mount . Bạn có giải pháp cho vấn đề với let mounted = true
trong hiệu ứng của bạn để tìm nạp dữ liệu, nhưng giải pháp sẽ không hoạt động đối với handleSubmit
, vì nó không phải là hiệu ứng. Bạn không thể trả về một hàm để đặt giá trị thành false khi nó được ngắt kết nối. Hơn nữa, sẽ không hiệu quả nếu thêm cùng một kiểm tra cho mọi chức năng.
Để giải quyết vấn đề này, bạn có thể tạo một biến chia sẻ được sử dụng bởi nhiều chức năng bằng cách nâng mounted
ra khỏi useEffect
Hook và giữ nó ở mức của thành phần. Bạn sẽ vẫn sử dụng hàm để đặt giá trị thành false
khi kết thúc useEffect
.
Bên trong App.js
, khai báo mounted
vào khi bắt đầu hàm. Sau đó, kiểm tra xem thành phần đã được mount chưa trước khi cài đặt dữ liệu trong các chức năng không đồng bộ khác. Đảm bảo loại bỏ khai báo mounted
bên trong hàm useEffect
:
import React, { useEffect, useState } from 'react'; import './App.css'; import { getList, setItem } from '../../services/list'; function App() { const [alert, setAlert] = useState(false); const [itemInput, setItemInput] = useState(''); const [list, setList] = useState([]); let mounted = true; useEffect(() => { if(list.length && !alert) { return; } getList() .then(items => { if(mounted) { setList(items) } }) return () => mounted = false; }, [alert, list]) useEffect(() => { if(alert) { setTimeout(() => { if(mounted) { setAlert(false); } }, 1000) } }, [alert]) const handleSubmit = (e) => { e.preventDefault(); setItem(itemInput) .then(() => { if(mounted) { setItemInput(''); setAlert(true); } }) }; return( <div className="wrapper"> ... </div> ) } export default App;
Khi bạn thực hiện thay đổi, bạn sẽ nhận được lỗi trong terminal nơi bạn đang chạy ứng dụng React của bạn :
ErrorAssignments to the 'mounted' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect react-hooks/exhaustive-deps
React đang cảnh báo bạn rằng các biến không ổn định. Khi nào có kết xuất lại, nó sẽ tính toán lại biến. Thông thường, điều này sẽ đảm bảo thông tin cập nhật. Trong trường hợp này, bạn đang dựa vào biến đó để tồn tại.
Giải pháp là một Hook khác được gọi là useRef
. useRef
Hook sẽ duy trì một biến trong suốt thời gian tồn tại của thành phần. Bí quyết duy nhất là lấy giá trị bạn cần để sử dụng thuộc tính .current
.
Bên trong App.js
, chuyển đổi mounted
thành tham chiếu bằng useRef
Hook. Sau đó, chuyển đổi từng cách sử dụng mounted
thành mounted.current
:
import React, { useEffect, useRef, useState } from 'react'; import './App.css'; import { getList, setItem } from '../../services/list'; function App() { const [alert, setAlert] = useState(false); const [itemInput, setItemInput] = useState(''); const [list, setList] = useState([]); const mounted = useRef(true); useEffect(() => { mounted.current = true; if(list.length && !alert) { return; } getList() .then(items => { if(mounted.current) { setList(items) } }) return () => mounted.current = false; }, [alert, list]) useEffect(() => { if(alert) { setTimeout(() => { if(mounted.current) { setAlert(false); } }, 1000) } }, [alert]) const handleSubmit = (e) => { e.preventDefault(); setItem(itemInput) .then(() => { if(mounted.current) { setItemInput(''); setAlert(true); } }) }; return( <div className="wrapper"> ... </div> ) } export default App;
Ngoài ra, hãy thận trọng về việc đặt biến trong hàm dọn dẹp để sử useEffect
. Chức năng dọn dẹp sẽ luôn chạy trước khi hiệu ứng chạy lại. Điều đó nghĩa là hàm dọn dẹp () => mounted.current = false
sẽ chạy mỗi khi alert
hoặc list
thay đổi. Để tránh bất kỳ kết quả sai nào, hãy đảm bảo cập nhật mounted.current
thành true
khi bắt đầu hiệu ứng. Sau đó, bạn có thể chắc chắn rằng nó sẽ chỉ được đặt thành false
khi thành phần được ngắt kết nối.
Lưu và đóng file . Khi trình duyệt làm mới, bạn có thể lưu các mục danh sách mới:
Lưu ý: Việc vô tình chạy lại một API nhiều lần là một vấn đề phổ biến. Mỗi khi một thành phần bị xóa và sau đó được gắn lại, bạn sẽ chạy lại tất cả quá trình tìm nạp dữ liệu ban đầu. Để tránh điều này, hãy xem xét phương pháp lưu vào bộ nhớ đệm cho các API đặc biệt nặng hoặc chậm. Bạn có thể sử dụng bất cứ thứ gì từ ghi nhớ các cuộc gọi dịch vụ, đến bộ nhớ đệm với nhân viên dịch vụ , cho đến một Hook tùy chỉnh. Có một số Hooks tùy chỉnh phổ biến cho các cuộc gọi dịch vụ bộ nhớ đệm, bao gồm useSWR và truy vấn phản ứng .
Cho dù bạn sử dụng cách tiếp cận nào, hãy chắc chắn xem xét cách bạn sẽ làm mất hiệu lực bộ nhớ cache vì đôi khi bạn cần tìm nạp dữ liệu mới nhất.
Trong bước này, bạn đã gửi dữ liệu đến một API. Bạn đã học cách cập nhật user khi dữ liệu được gửi và cách kích hoạt làm mới dữ liệu danh sách của bạn. Bạn cũng tránh cài đặt dữ liệu trên các thành phần chưa được mount bằng cách sử dụng useRef
Hook để lưu trữ trạng thái của thành phần để nó được dùng bởi nhiều dịch vụ.
Kết luận
API cung cấp cho bạn khả năng kết nối với nhiều dịch vụ hữu ích. Chúng cho phép bạn lưu trữ và truy xuất dữ liệu ngay cả sau khi user đóng trình duyệt của họ hoặc ngừng sử dụng ứng dụng. Với mã được tổ chức tốt, bạn có thể cô lập các dịch vụ của bạn khỏi các thành phần của bạn để các thành phần của bạn có thể tập trung vào việc hiển thị dữ liệu mà không cần biết dữ liệu bắt nguồn từ đâu. API Web mở rộng ứng dụng của bạn vượt xa khả năng của phiên trình duyệt hoặc bộ nhớ. Họ mở ứng dụng của bạn cho toàn bộ thế giới công nghệ web.
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
Cách triển khai xác thực API bằng mã thông báo web JSON và hộ chiếu2020-09-24
Cách cài đặt và sử dụng GoAccess Web Log Analyzer trên Ubuntu 20.04
2020-09-15
Xây dựng ứng dụng web CRUD với Python và Flask - Phần thứ nhất
2020-09-15
Phông chữ có thể thay đổi trên web bằng CSS
2020-09-01
Làm thế nào để tạo một Web Scraper đồng thời với Puppeteer, Node.js, Docker và Kubernetes
2020-08-19
Cách tạo ứng dụng web tiến bộ với Angular
2020-07-09
Cách cài đặt Django Web Framework trên Ubuntu 20.04
2020-07-06
Cách tạo chế độ xem để phát triển web Django
2020-05-14
Cách tạo chế độ xem để phát triển web Django
2020-05-14
Cách tạo ứng dụng web bằng Flask trong Python 3
2020-04-16