Cách chuyển đổi các thành phần lớp React thành các thành phần chức năng bằng React Hooks
Bản phát hành alpha mới nhất của React đã giới thiệu một khái niệm mới gọi là Hooks . Hooks được đưa vào React để giải quyết các vấn đề chung . Tuy nhiên, chúng chủ yếu phục vụ như một sự thay thế cho các lớp học. Với Hooks, bạn có thể tạo các thành phần chức năng sử dụng các phương thức trạng thái và vòng đời.Hooks hiện có sẵn trong React v16.7.0-alpha. Không có kế hoạch để loại bỏ các lớp học. Hooks cung cấp một cách khác để viết React.
Cho rằng Hooks vẫn còn mới, nhiều nhà phát triển đang tìm cách áp dụng khái niệm này trong các ứng dụng React hiện có hoặc các ứng dụng mới của họ. Trong bài đăng này, bạn sẽ khám phá năm cách để chuyển đổi các thành phần lớp React thành các thành phần chức năng bằng cách sử dụng React Hooks.
Yêu cầu
Để hoàn thành hướng dẫn này, bạn cần :
- Làm quen với JavaScript. Bạn có thể xem lại loạt bài Cách viết mã trong JavaScript để tìm hiểu thêm và bắt đầu.
- Làm quen với React. Bạn có thể xem lại loạt bài Cách viết mã trong React.js của ta để biết hướng dẫn giúp bạn bắt đầu.
Không cần phát triển local , nhưng các ví dụ CodeSandbox được cung cấp để thử nghiệm thêm.
Bước 1 - Hiểu một lớp không có trạng thái hoặc phương thức vòng đời
Hãy bắt đầu với một lớp React không có trạng thái hoặc thành phần vòng đời:
import React, { Component } from 'react'; class App extends Component { alertName = () => { alert('John Doe'); }; render() { return ( <div> <h3>This is a Class Component</h3> <button onClick={this.alertName}> Alert </button> </div> ); } }; export default App;
Ở đây bạn có một lớp React điển hình, thiếu trạng thái hoặc phương thức vòng đời. Nó cảnh báo tên khi một nút được nhấp vào.
Chức năng tương đương của lớp này sẽ như thế này:
import React from 'react'; function App() { const alertName = () => { alert('John Doe'); }; return ( <div> <h3>This is a Functional Component</h3> <button onClick={alertName}> Alert </button> </div> ); }; export default App;
Giống như ví dụ đầu tiên, lớp chức năng này hoạt động theo một cách điển hình.
Tuy nhiên, ví dụ này không sử dụng Hooks hoặc bất kỳ thứ gì mới. Trong những ví dụ này, bạn không cần trạng thái hoặc vòng đời.
Ta hãy xem xét các thành phần dựa trên lớp với trạng thái và tìm hiểu cách chuyển đổi chúng thành các thành phần chức năng bằng cách sử dụng Hooks.
Bước 2 - Thêm móc vào các lớp có trạng thái
Hãy xem xét trường hợp bạn có biến tên chung mà bạn có thể cập nhật trong ứng dụng từ trường nhập văn bản.
Trong React, bạn xử lý các trường hợp như thế này bằng cách xác định biến tên trong một đối tượng state
và gọi setState()
khi ta có một giá trị mới để cập nhật biến name
với:
import React, { Component } from 'react'; class App extends Component { state = { name: '' } alertName = () => { alert(this.state.name); }; handleNameInput = e => { this.setState({ name: e.target.value }); }; render() { return ( <div> <h3>This is a Class Component</h3> <input type="text" onChange={this.handleNameInput} value={this.state.name} placeholder="Your Name" /> <button onClick={this.alertName}> Alert </button> </div> ); } } export default App;
Khi user nhập tên vào trường nhập và nhấp vào nút Cảnh báo , nó sẽ bật lên một cảnh báo với tên được xác định trong trạng thái.
Bạn có thể chuyển đổi toàn bộ lớp này thành một thành phần React chức năng bằng cách sử dụng Hooks:
import React, { useState } from 'react'; function App() { const [name, setName] = useState('John Doe'); const alertName = () => { alert(name); }; const handleNameInput = e => { setName(e.target.value); }; return ( <div> <h3>This is a Functional Component</h3> <input type="text" onChange={handleNameInput} value={name} placeholder="Your Name" /> <button onClick={alertName}> Alert </button> </div> ); }; export default App;
Ở đây, bạn đã giới thiệu useState
Hook. Nó cho phép bạn sử dụng trạng thái trong các thành phần chức năng của React. Với useState()
, bạn có thể sử dụng state trong thành phần chức năng này. Nó sử dụng một cú pháp tương tự với phép gán cấu trúc cho các mảng.
Hãy xem xét dòng này:
const [name, setName] = useState('John Doe')
Ở đây, name
tương đương với this.state
trong một thành phần lớp bình thường và setName
tương đương với this.setState
.
Giá trị ban đầu của trạng thái trong useState()
đến từ một đối số. Nói cách khác, đối số useState()
là giá trị ban đầu của trạng thái. Trong trường hợp của bạn, bạn đặt nó thành 'John Doe'
. Điều này nghĩa là trạng thái ban đầu của tên trong tiểu bang là 'John Doe'
.
Mã này là một ví dụ về cách bạn có thể chuyển đổi một thành phần React dựa trên lớp có trạng thái thành một thành phần chức năng bằng cách sử dụng Hooks.
Hãy khám phá các kịch bản khác, bao gồm các lớp có nhiều thuộc tính trạng thái.
Bước 3 - Thêm Hook vào các lớp có nhiều thuộc tính trạng thái
Bạn đã xem cách bạn có thể chuyển đổi một thuộc tính trạng thái với useState
, nhưng cách tiếp cận tương tự sẽ không hoàn toàn hiệu quả khi bạn có nhiều thuộc tính trạng thái. Ví dụ: nếu bạn có hai hoặc nhiều trường đầu vào cho userName
, firstName
và lastName
, thì bạn sẽ có một thành phần dựa trên lớp với ba thuộc tính trạng thái:
import React, { Component } from 'react'; class App extends Component { state = { userName: '', firstName: '', lastName: '' }; logName = () => { console.log(this.state.userName); console.log(this.state.firstName); console.log(this.state.lastName); }; handleUserNameInput = e => { this.setState({ userName: e.target.value }); }; handleFirstNameInput = e => { this.setState({ firstName: e.target.value }); }; handleLastNameInput = e => { this.setState({ lastName: e.target.value }); }; render() { return ( <div> <h3>This is a Class Component</h3> <input type="text" onChange={this.handleUserNameInput} value={this.state.userName} placeholder="Your Username" /> <input type="text" onChange={this.handleFirstNameInput} value={this.state.firstName} placeholder="Your First Name" /> <input type="text" onChange={this.handleLastNameInput} value={this.state.lastName} placeholder="Your Last Name" /> <button className="btn btn-large right" onClick={this.logName} > Log Names </button> </div> ); } } export default App;
Để chuyển đổi lớp này thành một thành phần chức năng với Hooks, bạn sẽ phải thực hiện một con đường hơi độc đáo. Sử dụng useState()
, ví dụ trên có thể được viết như sau:
import React, { useState } from 'react'; function App() { const [userName, setUsername] = useState(''); const [firstName, setFirstname] = useState(''); const [lastName, setLastname] = useState(''); const logName = () => { console.log(userName); console.log(firstName); console.log(lastName); }; const handleUserNameInput = e => { setUsername(e.target.value); }; const handleFirstNameInput = e => { setFirstname(e.target.value); }; const handleLastNameInput = e => { setLastname(e.target.value); }; return ( <div> <h3>This is a Functional Component</h3> <input type="text" onChange={handleUserNameInput} value={userName} placeholder="Your Username" /> <input type="text" onChange={handleFirstNameInput} value={firstName} placeholder="Your First Name" /> <input type="text" onChange={handleLastNameInput} value={lastName} placeholder="Your Last Name" /> <button className="btn btn-large right" onClick={logName} > Log Names </button> </div> ); }; export default App;
Đây là CodeSandbox cho ví dụ này.
Điều này giải thích cách bạn có thể chuyển đổi một thành phần dựa trên lớp có nhiều thuộc tính trạng thái thành một thành phần chức năng bằng cách sử dụng useState()
.
Bước 4 - Thêm Hook vào một lớp với State và componentDidMount
Hãy xem xét một lớp với state
và componentDidMount
. Để chứng minh, bạn sẽ xem xét một tình huống trong đó bạn đặt trạng thái ban đầu cho ba trường đầu vào và tất cả chúng được cập nhật thành một bộ giá trị khác sau năm giây.
Để làm điều này, bạn sẽ khai báo giá trị trạng thái ban đầu cho các trường đầu vào và triển khai phương thức vòng đời componentDidMount()
sẽ chạy sau lần hiển thị ban đầu để cập nhật các giá trị trạng thái:
import React, { Component } from 'react'; class App extends Component { state = { // initial state userName: 'johndoe', firstName: 'John', lastName: 'Doe' } componentDidMount() { setInterval(() => { this.setState({ // update state userName: 'janedoe', firstName: 'Jane', lastName: 'Doe' }); }, 5000); } logName = () => { console.log(this.state.userName); console.log(this.state.firstName); console.log(this.state.lastName); }; handleUserNameInput = e => { this.setState({ userName: e.target.value }); }; handleFirstNameInput = e => { this.setState({ firstName: e.target.value }); }; handleLastNameInput = e => { this.setState({ lastName: e.target.value }); }; render() { return ( <div> <h3>This is a Class Component</h3> <input type="text" onChange={this.handleUserNameInput} value={this.state.userName} placeholder="Your Username" /> <input type="text" onChange={this.handleFirstNameInput} value={this.state.firstName} placeholder="Your First Name" /> <input type="text" onChange={this.handleLastNameInput} value={this.state.lastName} placeholder="Your Last Name" /> <button className="btn btn-large right" onClick={this.logName} > Log Names </button> </div> ); } } export default App;
Khi ứng dụng chạy, các trường đầu vào sẽ có các giá trị ban đầu bạn đã xác định trong đối tượng trạng thái. Các giá trị này sau đó sẽ cập nhật thành các giá trị bạn đã xác định bên trong phương thức componentDidMount()
sau năm giây.
Tiếp theo, bạn sẽ chuyển đổi lớp này thành một thành phần chức năng bằng cách sử dụng React useState
và useEffect
Hooks:
import React, { useState, useEffect } from 'react'; function App() { const [userName, setUsername] = useState('johndoe'); const [firstName, setFirstname] = useState('John'); const [lastName, setLastname] = useState('Doe'); useEffect(() => { setInterval(() => { setUsername('janedoe'); setFirstname('Jane'); setLastname('Doe'); }, 5000); }); const logName = () => { console.log(userName); console.log(firstName); console.log(lastName); }; const handleUserNameInput = e => { setUsername({ userName: e.target.value }); }; const handleFirstNameInput = e => { setFirstname({ firstName: e.target.value }); }; const handleLastNameInput = e => { setLastname({ lastName: e.target.value }); }; return ( <div> <h3>This is a Functional Component</h3> <input type="text" onChange={handleUserNameInput} value={userName} placeholder="Your Username" /> <input type="text" onChange={handleFirstNameInput} value={firstName} placeholder="Your First Name" /> <input type="text" onChange={handleLastNameInput} value={lastName} placeholder="Your Last Name" /> <button className="btn btn-large right" onClick={logName} > Log Names </button> </div> ); }; export default App;
Đây là CodeSandbox cho ví dụ này.
Về chức năng, thành phần này hoạt động giống hệt như ví dụ trước. Sự khác biệt duy nhất là thay vì sử dụng đối tượng state
thông thường và phương thức vòng đời componentDidMount()
như bạn đã làm trong thành phần lớp, bạn đã sử dụng useState
và useEffect
Hooks.
Bước 5 - Thêm Hook vào một lớp có State, componentDidMount
và componentDidUpdate
Tiếp theo, hãy xem xét một lớp React với trạng thái và hai phương thức vòng đời: componentDidMount
và componentDidUpdate
. Hầu hết các giải pháp cho đến thời điểm này đều sử dụng useState
Hook. Trong ví dụ này, bạn sẽ tập trung vào useEffect
Hook.
Để chứng minh tốt nhất cách hoạt động của điều này, hãy sửa đổi mã của bạn để cập nhật động tiêu đề <h3>
trên trang.
Hiện tại, tiêu đề cho biết This is a Class Component
. Bây giờ, bạn sẽ xác định một phương thức componentDidMount()
để cập nhật tiêu đề Welcome to React Hooks
sau ba giây:
import React, { Component } from 'react'; class App extends Component { state = { header: 'Welcome to React Hooks' } componentDidMount() { const header = document.querySelectorAll('#header')[0]; setTimeout(() => { header.innerHTML = this.state.header; }, 3000); } render() { return ( <div> <h3 id="header">This is a Class Component</h3> </div> ); } } export default App;
Khi ứng dụng chạy, nó sẽ bắt đầu với tiêu đề ban đầu This is a Class Component
và thay đổi thành Welcome to React Hooks
sau ba giây. Đây là hành vi componentDidMount()
cổ điển vì nó chạy sau khi hàm render
được thực thi thành công.
Hãy thêm chức năng cập nhật động tiêu đề từ một trường nhập khác để tiêu đề được cập nhật văn bản mới trong khi bạn nhập.
Để thực hiện điều này, bạn cần triển khai phương thức vòng đời componentDidUpdate()
:
import React, { Component } from 'react'; class App extends Component { state = { header: 'Welcome to React Hooks' } componentDidMount() { const header = document.querySelectorAll('#header')[0]; setTimeout(() => { header.innerHTML = this.state.header; }, 3000); } componentDidUpdate() { const node = document.querySelectorAll('#header')[0]; node.innerHTML = this.state.header; } handleHeaderInput = e => { this.setState({ header: e.target.value }); }; render() { return ( <div> <h3 id="header">This is a Class Component</h3> <input type="text" onChange={this.handleHeaderInput} value={this.state.header} /> </div> ); } } export default App;
Ở đây, bạn có state
, componentDidMount()
và componentDidUpdate()
. Khi bạn chạy ứng dụng, hàm componentDidMount()
sẽ cập nhật tiêu đề Welcome to React Hooks
sau ba giây. Khi bạn bắt đầu nhập vào trường nhập văn bản tiêu đề, văn bản <h3>
sẽ cập nhật với văn bản đầu vào như được xác định trong phương thức componentDidUpdate()
.
Tiếp theo, bạn sẽ chuyển đổi lớp này thành một thành phần chức năng bằng useEffect()
:
import React, { useState, useEffect } from 'react'; function App() { const [header, setHeader] = useState('Welcome to React Hooks'); useEffect(() => { const newheader = document.querySelectorAll('#header')[0]; setTimeout(() => { newheader.innerHTML = header; }, 3000); }); const handleHeaderInput = e => { setHeader(e.target.value); }; return ( <div> <h3 id="header">This is a Functional Component</h3> <input type="text" onChange={handleHeaderInput} value={header} /> </div> ); }; export default App;
Kiểm tra ví dụ này trên CodeSandbox .
Bạn đã đạt được chức năng tương tự với thành phần này như trước đây bằng cách sử dụng useEffect()
. Bạn cũng đã tối ưu hóa mã, vì bạn không phải viết mã riêng biệt cho các hàm componentDidMount()
và componentDidUpdate()
. Với useEffect()
Hook, bạn có được chức năng của cả hai. Điều này là do useEffect()
chạy cả sau lần hiển thị ban đầu và sau mỗi lần cập nhật tiếp theo theo mặc định.
Bước 6 - Chuyển đổi PureComponent
thành React memo
React PureComponent hoạt động theo cách tương tự như Component . Sự khác biệt chính giữa chúng là React.Component
không triển khai phương thức vòng đời shouldComponentUpdate()
trong khi React.PureComponent
thì có.
Nếu bạn có một ứng dụng mà hàm render()
hiển thị cùng một kết quả với cùng một trạng thái và đạo cụ, bạn có thể sử dụng React.PureComponent
để tăng hiệu suất trong một số trường hợp.
React.memo()
hoạt động theo cách tương tự. Khi thành phần hàm của bạn hiển thị cùng một kết quả với cùng một đạo cụ, bạn có thể gói nó trong một lệnh gọi tới React.memo()
để nâng cao hiệu suất. Việc sử dụng PureComponent
và React.memo()
mang lại cho các ứng dụng React sự gia tăng đáng kể về hiệu suất vì nó làm giảm số lượng hoạt động kết xuất trong ứng dụng.
Để hiểu những gì cả hai đều làm, trước tiên bạn sẽ xem mã trong đó một thành phần hiển thị hai giây một lần, cho dù có thay đổi về giá trị hoặc trạng thái hay không:
import React, { Component } from 'react'; function Unstable(props) { // monitor how many times this component is rendered console.log('Rendered Unstable component'); return ( <div> <p>{props.value}</p> </div> ); }; class App extends Component { state = { value: 1 }; componentDidMount() { setInterval(() => { this.setState(() => { return { value: 1 }; }); }, 2000); } render() { return ( <div> <Unstable value={this.state.value} /> </div> ); } } export default App;
Khi bạn chạy ứng dụng và kiểm tra log , bạn sẽ nhận thấy rằng nó hiển thị thành phần sau mỗi hai giây, mà không có bất kỳ thay đổi nào về trạng thái hoặc đạo cụ. Đây là tình huống mà bạn có thể cải thiện bằng cả PureComponent
và React.memo()
.
Hầu hết thời gian, bạn chỉ muốn kết xuất lại một thành phần khi có sự thay đổi về trạng thái hoặc đạo cụ. Sử dụng ví dụ trên, bạn có thể cải thiện nó bằng PureComponent
để thành phần chỉ hiển thị lại khi có thay đổi về trạng thái hoặc đạo cụ.
Bạn có thể thực hiện điều này bằng lệnh PureComponent
và mở rộng nó:
import React, { PureComponent } from 'react'; function Unstable(props) { console.log('Rendered Unstable component'); return ( <div> <p>{props.value}</p> </div> ); }; class App extends PureComponent { state = { value: 1 }; componentDidMount() { setInterval(() => { this.setState(() => { return { value: 1 }; }); }, 2000); } render() { return ( <div> <Unstable value={this.state.value} /> </div> ); } } export default App;
Bây giờ, nếu bạn chạy lại ứng dụng, bạn chỉ nhận được kết xuất ban đầu. Không có gì khác xảy ra sau đó. Điều này là do bạn có class App extends PureComponent {}
thay vì class App extends Component {}
.
Điều này giải quyết vấn đề các thành phần được kết xuất lại mà không liên quan đến trạng thái hiện tại. Tuy nhiên, nếu bạn thực hiện thay đổi trạng thái trong phương thức setState
của bạn , bạn sẽ gặp phải vấn đề khác.
Ví dụ: hãy xem xét các thay đổi sau đối với setState()
:
Hiện tại, value
được đặt thành 1
:
componentDidMount() { setInterval(() => { this.setState(() => { return { value: 1 }; }); }, 2000); }
Hãy xem xét một tình huống trong đó value
được đặt thành Math.random()
:
componentDidMount() { setInterval(() => { this.setState(() => { return { value: Math.round(Math.random()) }; }); }, 2000); }
Trong trường hợp này, thành phần ví dụ đầu tiên sẽ hiển thị lại mỗi khi giá trị cập nhật thành số ngẫu nhiên tiếp theo. Tuy nhiên, PureComponent
cho phép bạn chỉ re-render các thành phần khi có sự thay đổi về trạng thái hoặc đạo cụ.
Đến đây bạn có thể khám phá cách sử dụng React.memo()
để đạt được bản sửa lỗi tương tự. Để thực hiện điều này, hãy bọc thành phần bằng React.memo()
:
import React, { Component } from 'react'; const Unstable = React.memo(function Unstable (props) { console.log('Rendered Unstable component'); return ( <div> <p>{props.value}</p> </div> ); }); class App extends Component { state = { val: 1 }; componentDidMount() { setInterval(() => { this.setState(() => { return { value: 1 }; }); }, 2000); } render() { return ( <div> <Unstable val={this.state.val} /> </div> ); } } export default App;
Đây là CodeSandbox cho ví dụ này.
Điều này đạt được kết quả tương tự như sử dụng PureComponent
. Thành phần chỉ hiển thị sau lần hiển thị ban đầu và không hiển thị lại cho đến khi có sự thay đổi về trạng thái hoặc đạo cụ.
Kết luận
Trong hướng dẫn này, bạn đã khám phá một số cách tiếp cận để che giấu một thành phần dựa trên lớp hiện có với một thành phần chức năng bằng cách sử dụng React Hooks.
Bạn cũng đã xem xét một trường hợp đặc biệt của việc chuyển đổi một lớp React PureComponent
thành React.memo()
.
Để sử dụng Hooks trong các ứng dụng của bạn, hãy nhớ cập nhật version React của bạn lên version được hỗ trợ:
"react": "^16.7.0-alpha", "react-dom": "^16.7.0-alpha",
Đến đây bạn đã có nền tảng để thử nghiệm thêm với React Hooks.
Tìm hiểu thêm về Bắt đầu với React Hooks và Xây dựng ứng dụng React To-Do với React Hooks .
Các tin liên quan