Thứ tư, 16/09/2020 | 00:00 GMT+7

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 :

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:

Ví dụClassComponent.js
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:

Ví dụF Chức năngComponent.js
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:

Ví dụClassComponentWithState.js
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:

Ví dụF Chức năngComponentWithState.js
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 , firstNamelastName , 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:

Ví dụClassComponentWithMultipleStateProperties.js
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:

Ví dụF Chức năngComponentWithMultipleStateProperties.js
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 statecomponentDidMount . Để 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:

Ví dụClassComponentWithStateAndComponentDidMount.js
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 useStateuseEffect Hooks:

Ví dụF Chức năngComponentWithStateAndComponentDidMount.js
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 useStateuseEffect Hooks.

Bước 5 - Thêm Hook vào một lớp có State, componentDidMountcomponentDidUpdate

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: componentDidMountcomponentDidUpdate . 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:

Ví dụClassComponentWithStateAndTwoLifecycleMethods.js
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() :

Ví dụClassComponent.js
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()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() :

Ví dụF Chức năngComponentWithStateAndTwoLifecycleMethods.js
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()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 PureComponentReact.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:

Ví dụClassComponent.js
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ả PureComponentReact.memo() .

Đầu ra log  console  cho nhiều hoạt động kết xuất

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ó:

Ví dụ PureComponent.js
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 {} .

Đầu ra log  console  cho một hoạt động kết xuất duy nhất

Đ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() :

Ví dụReactMemo.js
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 HooksXây dựng ứng dụng React To-Do với React Hooks .


Tags:

Các tin liên quan