5 Cách chuyển Class Component thành Functional Component với React Hooks

Bản phát hành alpha của React đã giới thiệu một khái niệm mới có tên là Hooks. Hooks được giới thiệu cho React để giải quyết các vấn đề phổ biến; tuy nhiên, chúng chủ yếu phục vụ như là một thay thế cho các class. Với Hooks, bạn có thể tạo các function component sử dụng các phương thức state và props.


1077

Class không có State hoặc Lifecycle

Để bắt đầu, chúng ta xem class React không có các component state cũng như lifecycle(vòng đời). Class sẽ thông báo tên khi người dùng click vào button:

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 chúng ta có class React điển hình, thiếu state hoặc phương thức lifecycle. Nó thông báo tên khi click vào button. Function tương đương của class này sẽ trông 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, class function này giống như cách điển hình. Chúng ta không sử dụng Hooks hoặc bất cứ thứ gì mới. Trong các ví dụ này, chúng ta không cần state hoặc lifecycle.

Chúng ta có thể thay đổi điều đó và kiểm tra các tình huống trong đó. Có class component với state và tìm hiểu cách chuyển nó thành function component bằng cách sử dụng Móc.

Thêm Hooks vào Class với State

Hãy cùng xem trường hợp này. Trong đó chúng ta có biến global name mà có thể cập nhật trong app từ field input. Trong React, chúng ta xử lý các trường hợp như thế này bằng cách xác định biến name trong state và gọi setState () khi chúng ta có một giá trị mới để cập nhật biến name

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 người dùng nhập name vào field input và click vào nút Alert, nó sẽ thông báo name mà đã xác định ở trạng thái.

Chúng ta có thể chuyển đổi toàn bộ class này thành function component React bằng cách sử dụng Hook:

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, chúng ta đã biết Hook useState . Nó cho phép chúng ta sử dụng state trong các function component React. Với Hook useState (), chúng ta có thể sử dụng state trong function component này. Nó sử dụng một cú pháp tương tự với việc gán cho các mảng. Được sử dụng như sau

const [name, setName] = useState("John Doe")

Ở đây, name tương đương với this.state ở class component trong khi setName tương đương với this.setState.

Thêm Hooks vào Class với nhiều State

Chúng ta đã biết cách chuyển thuộc tính state với useState, nhưng cách này khá hiệu quả khi bạn có nhiều thuộc tính state. Ví dụ: nếu chúng ta có hai hoặc nhiều field input cho userName, FirstName và lastName, thì chúng ta sẽ có một class component với ba thuộc tính state:

import React, { Component } from 'react';
class App extends Component {
    state = {
      userName: '',
      firstName: '',
      lastName: ''
    };
  logName = () => {
    // do whatever with the names ... let's just log them here
    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 firstname"
        />
        <input
          type="text"
          onChange={this.handleLastNameInput}
          value={this.state.lastName}
          placeholder="Your lastname"
        />
        <button className="btn btn-large right" onClick={this.logName}>
          {' '}
          Log Names{' '}
        </button>
      </div>
    );
  }
}
export default App;

Để chuyển đổi class này thành function component với Hooks, chúng ta sẽ phải thực hiện cách khác. Sử dụng Hook useState (), ví dụ trên có thể được viết là:

import React, { useState } from 'react';
function App() {
  const [userName, setUsername] = useState('');
  const [firstName, setFirstname] = useState('');
  const [lastName, setLastname] = useState('');
  const logName = () => {
    // do whatever with the names... let's just log them here
    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="username..."
      />
      <input
        type="text"
        onChange={handleFirstNameInput}
        value={firstName}
        placeholder="firstname..."
      />
      <input
        type="text"
        onChange={handleLastNameInput}
        value={lastName}
        placeholder="lastname..."
      />
      <button className="btn btn-large right" onClick={logName}>
        {' '}
        Log Names{' '}
      </button>
    </div>
  );
};
export default App;

Điều này cho thấy cách chúng ta có thể chuyển class component với nhiều state thành function component bằng cách sử dụng Hook useState ().

Tham khảo ví dụ tại đây Codesandbox 

Thêm Hooks vào Class với State và componentDidMount

Hãy xem class với state  và componentDidMount. Để chứng minh, chúng ta sẽ xem trường hợp sau, trong đó chúng ta đặt state ban đầu cho ba field input và để chúng cập nhật thành giá trị khác sau năm giây.

import React, { Component, useEffect } from 'react';
class App extends Component {
    state = {
      // initial state
      userName: 'JD',
      firstName: 'John',
      lastName: 'Doe'
  }
  componentDidMount() {
    setInterval(() => {
      this.setState({
        // update state
        userName: 'MJ',
        firstName: 'Mary',
        lastName: 'jane'
      });
    }, 5000);
  }
  logName = () => {
    // do whatever with the names ... let's just log them here
    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> The text fields will update in 5 seconds </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 firstname"
        />
        <input
          type="text"
          onChange={this.handleLastNameInput}
          value={this.state.lastName}
          placeholder="Your lastname"
        />
        <button className="btn btn-large right" onClick={this.logName}>
          {' '}
          Log Names{' '}
        </button>
      </div>
    );
  }
}
export default App;

Khi ứng dụng chạy, các fields input sẽ có các giá trị ban đầu mà xác định trong state. Các giá trị này sau đó sẽ cập nhật thành các giá trị mài đã xác định bên trong componentDidMount() sau năm giây.

Tiếp theo, chúng ta chuyển class này thành function component bằng cách sử dụng React useState và useEffect Hook:

import React, { useState, useEffect } from 'react';
function App() {
  const [userName, setUsername] = useState('JD');
  const [firstName, setFirstname] = useState('John');
  const [lastName, setLastname] = useState('Doe');
  useEffect(() => {
    setInterval(() => {
      setUsername('MJ');
      setFirstname('Mary');
      setLastname('Jane');
    }, 5000);
  });
  const logName = () => {
    // do whatever with the names ...
    console.log(this.state.userName);
    console.log(this.state.firstName);
    console.log(this.state.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> The text fields will update in 5 seconds </h3>
      <input
        type="text"
        onChange={handleUserNameInput}
        value={userName}
        placeholder="Your username"
      />
      <input
        type="text"
        onChange={handleFirstNameInput}
        value={firstName}
        placeholder="Your firstname"
      />
      <input
        type="text"
        onChange={handleLastNameInput}
        value={lastName}
        placeholder="Your lastname"
      />
      <button className="btn btn-large right" onClick={logName}>
        {' '}
        Log Names{' '}
      </button>
    </div>
  );
};
export default App;

Thêm Hooks vào Class với State, componentDidMount, và componentDidUpdate

Tiếp theo, hãy để class React với state và hai lifecycle: componentDidMount  và componentDidUpdate. Bạn có thể nhận thấy rằng chúng ta đã sử dụng với useState Hook. Trong ví dụ này, chúng ta tập trung vào Hook useEffect .

Để thực hiện tốt nhất cách thức hoạt động của nó, hãy để update code động tiêu đề <h3> trên trang. Hiện tại tiêu đề cho biết This is a Class Component. Bây giờ, chúng ta sẽ định nghĩa componentDidMount() để cập nhật tiêu đề để nói 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);
  }
  logName = () => {
    // do whatever with the names ...
  };
  // { ... }
  render() {
    return (
      <div>
        <h3 id="header"> 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 firstname"
        />
        <input
          type="text"
          onChange={this.handleLastNameInput}
          value={this.state.lastName}
          placeholder="Your lastname"
        />
        <button className="btn btn-large right" onClick={this.logName}>
          {' '}
          Log Names{' '}
        </button>
      </div>
    );
  }
}
export default App;

Điều gì xảy ra nếu chúng ta muốn cập nhật động tiêu đề từ field input, để tiêu đề được cập nhật text mới trong khi vừa được nhập? Để làm điều đó, chúng ta cần phải thực hiện phương thức lifecycle componentDidUpdate như thế nà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);
  }
  componentDidUpdate() {
    const node = document.querySelectorAll('#header')[0];
    node.innerHTML = this.state.header;
  }
  logName = () => {
    // do whatever with the names ... let's just log them here
    console.log(this.state.username);
  };
  // { ... }
  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.handleUserNameInput}
          value={this.state.userName}
          placeholder="Your username"
        />
        <input
          type="text"
          onChange={this.handleFirstNameInput}
          value={this.state.firstName}
          placeholder="Your firstname"
        />
        <input
          type="text"
          onChange={this.handleLastNameInput}
          value={this.state.lastName}
          placeholder="Your lastname"
        />
        <button className="btn btn-large right" onClick={this.logName}>
          {' '}
          Log Names{' '}
        </button>
        <input
          type="text"
          onChange={this.handleHeaderInput}
          value={this.state.header}
        />{' '}
      </div>
    );
  }
}
export default App;

Tiếp theo, chúng ta chuyển class này thành function component với Hook useEffect ():

import React, { useState, useEffect } from 'react';
function App() {
  const [userName, setUsername] = useState('');
  const [firstName, setFirstname] = useState('');
  const [lastName, setLastname] = useState('');
  const [header, setHeader] = useState('Welcome to React Hooks');
  const logName = () => {
    // do whatever with the names...
    console.log(userName);
  };
  useEffect(() => {
    const newheader = document.querySelectorAll('#header')[0];
    setTimeout(() => {
      newheader.innerHTML = header;
    }, 3000);
  });
  const handleUserNameInput = e => {
    setUsername(e.target.value);
  };
  const handleFirstNameInput = e => {
    setFirstname(e.target.value);
  };
  const handleLastNameInput = e => {
    setLastname(e.target.value);
  };
  const handleHeaderInput = e => {
    setHeader(e.target.value);
  };
  return (
    <div>
      <h3 id="header"> This is a functional Component </h3>
      <input
        type="text"
        onChange={handleUserNameInput}
        value={userName}
        placeholder="username..."
      />
      <input
        type="text"
        onChange={handleFirstNameInput}
        value={firstName}
        placeholder="firstname..."
      />
      <input
        type="text"
        onChange={handleLastNameInput}
        value={lastName}
        placeholder="lastname..."
      />
      <button className="btn btn-large right" onClick={logName}>
        {' '}
        Log Names{' '}
      </button>
      <input type="text" onChange={handleHeaderInput} value={header} />
    </div>
  );
};
export default App;

Chúng ta đã có function với component này như đã làm với các phần trước bằng cách sử dụng Hook useEffect (). Chúng ta cũng đã tối ưu hóa code, vì không phải viết code riêng cho các componentDidMount và functions componentDidUpdate(). Với Hook useEffect (), chúng ta có được function của cả hai. Điều này là do useEffect () chạy cả sau render ban đầu và sau mỗi lần update tiếp theo.

Chuyển PureComponent thành React memo

React PureComponent hoạt động theo cách tương tự Component. Sự khác biệt chính giữa chúng là React.Component không thực hiện phương thức lifecycle ShouldComponentUpdate () trong khi React.PureComponent thực hiện được.

React.memo() hoạt động theo cách tương tự. Khi function component hiển thị cùng kết quả được cung cấp cùng props,gói nó trong React.memo () để nâng cao hiệu suất. Việc sử dụng PureComponent và React.memo() giúp các ứng dụng React tăng hiệu suất đáng kể vì nó làm giảm số lượng hoạt động render trong ứng dụng.

import React, { Component } from 'react';
function Unstable(props) {
  // monitor how many times this component is rendered
  console.log(' Rendered this 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 app và kiểm tra logs, thấy hiển thị component sau mỗi hai giây, mà không có bất kỳ thay đổi nào về state hoặc props. Đây là tình huống mà chúng ta có thể cải thiện với cả PureComponent và React.memo ().

Hầu hết thời gian, chúng ta chỉ muốn re-render lại component khi có sự thay đổi về state hoặc props. Cải thiện bằng PureComponent để component hiển thị lại khi thay đổi state hoặc props. Làm điều này bằng cách import PureComponent và mở rộng nó như thế này:

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 render ban đầu. Không có gì khác xảy ra sau đó. Điều này là do chúng ta có class App extends PureComponent{} thay vì class App extends Component{}

Điều này giải quyết vấn đề của các component được re-render mà không liên quan đến state hiện tại. Tuy nhiên, nếu chúng ta thực hiện thay đổi state trong phương thức setState của mình, sẽ gặp vấn đề. Ví dụ: hãy xem xét các thay đổi sau đối với setState()

componentDidMount() {
  setInterval(() => {
    this.setState(() => {
      return { value: 1 };
    });
  }, 2000);
}
componentDidMount() {
  setInterval(() => {
    this.setState(() => {
      return { value: Math.random() };
    });
  }, 2000);
}

Component sẽ re-render mỗi lần giá trị cập nhật lên số ngẫu nhiên tiếp theo, vì như chúng ta đã nói, PureComponent chỉ có thể re-render các component khi có thay đổi state hoặc props.

Bây giờ chúng ta có thể xem cách sử dụng React.memo () để có thể cùng bản sửa lỗi. 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>{props.val} </div>;
});
class App extends Component {
  state = {
    val: 1
  };
  componentDidMount() {
    setInterval(() => {
      this.setState({ val: 1 });
    }, 2000);
  }
  render() {
    return (
      <div>
        <header className="App-header">
          <Unstable val={this.state.val} />
        </header>
      </div>
    );
  }
}
export default App;

Chúng ta đã sử dụng PureComponentchuyển class thành function.

Kết luận

Trong bài viết này, chúng ta đã có 5 cách có thể chuyển đổi một class component thành function component bằng cách sử dụng React Hook.

Tham khảo thêm React : Tự học React JS thời đại dịch Corona nCoV – Phần 1


Like it? Share with your friends!

1077