當使用React框架開發的時候,有兩種方式建立元件,使用函式和使用類,目前函式元件越來越流行。本文將透過舉例的方式,分析函式元件和類元件的不同,帶你深入探索React的世界。
JSX渲染方式函式元件和類元件處理JSX的方式不同,就像他們的名字,函式元件是一個純Javascript函式,直接返回JSX;而類元件是一個Javascript類,透過擴充套件React.Component,並實現render方法,render方法中返回JSX。下面舉例說明:
import React from "react"; const FunctionalComponent = () => { return <h1>Hello, world</h1>;};
上面透過ES6箭頭函式的形式定義了一個函式元件,函式體內直接返回JSX。如果你對箭頭函式不熟悉,也可以寫成下面這種形式:
import React from "react";function FunctionalComponent() { return <h1>Hello, world</h1>;}
兩種寫法是一樣的。
然後,來看看如何定義類元件,首先我們需要擴充套件React.Component,然後在render方法中返回JSX,具體看下面的程式碼片段:
import React, { Component } from "react";class ClassComponent extends Component { render() { return <h1>Hello, world</h1>; }}
上面使用了ES6的解構賦值語法來匯入模組,如果你對解構賦值語法不熟悉,也可以寫成下面這種形式,會看上去更簡潔一些:
import React from "react";class ClassComponent extends React.Component { render() { return <h1>Hello, world</h1>; }}
傳遞props當需要向一個元件傳遞資料的時候,我們使用props,比如<FunctionalComponent name="Shiori" />,name就是Component的一個props屬性,這裡可以有更多屬性。FunctionalComponent元件的函式形式的定義如下:
const FunctionalComponent = ({ name }) => { return <h1>Hello, {name}</h1>;};
或者不使用解構賦值
const FunctionalComponent = (props) => { return <h1>Hello, {props.name}</h1>;};
這種方式,你需要使用props.name來獲取name屬性。
然後,我們來看看類元件如何使用props,
class ClassComponent extends React.Component { render() { const { name } = this.props; return <h1>Hello, { name }</h1>; }}
在類元件中,你需要使用this來獲取props,然後可以使用解構賦值獲取name屬性。
處理state在React專案中,我們不可避免的要處理狀態變數。類元件直到最近才支援處理狀態,然而,從React從16.8版本開始,函式元件支援鉤子方法useState,這樣我們可以很方便的在函式元件中使用狀態變數。下面透過一個counter計數器例項來說明它們的不同。
在函式元件中處理狀態變數const FunctionalComponent = () => { const [count, setCount] = React.useState(0); return ( <div> <p>count: {count}</p> <button onClick={() => setCount(count + 1)}>Click</button> </div> );};
這裡使用useState鉤子,它接收一個初始的state作為引數。在本例中,計數器從0開始,所以我們給count一個初始值0。
state的初始值支援各種資料型別,包括null,string或者object物件,只要是javascript允許的都可以。在=號的左邊,我們使用解構賦值的形式來接受useState的返回值,包括當前的狀態變數和更新該變數的setter函式,即count和setCount。
在類元件中處理狀態變數class ClassComponent extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>count: {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click </button> </div> ); }}
與函式元件大同小異,首先我們要理解React.Component的建構函式constructor,react的官方文件對constructor的定義如下:
“The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs.”
翻譯一下,
React元件的constructor方法會在元件完全載入完成之前呼叫。在constructor方法中,你需要在第一行呼叫super(props),否則會報this.props是undefined的錯誤。
如果在類元件中,你沒有實現constructor方法並呼叫super(props),那麼所有的狀態變數都將是undefined。所以,別忘記先定義constructor方法,在constructor方法中,我們需要給this.state一個初始值,像上面的程式碼那樣。然後我們可以在JSX中使用this.state.count來獲取count的值,setter的使用也是類似的。
這裡先定義一個onClick方法,後面會用到,
onClick={() => this.setState((state) => { return { count: state.count + 1 }; })}
這裡注意setState()方法接收的是個箭頭函式,而箭頭函式的引數是state和props,props是可選的,這裡沒用到就沒寫。
生命週期函式React的元件在它整個的渲染的過程中,有它的生命週期。如果你之前一直使用類元件,剛剛接觸函式元件,你可能會疑惑,為什麼在函式元件中沒有componentDidMount()這類的生命週期方法?但是別急,有其他的鉤子函式可以使用。
載入階段類元件的生命週期函式componentDidMount會在首次渲染完成之後呼叫。首次渲染完成之前會呼叫componentWillMount ,但是這個方法在新版本的React中不推薦使用了。
在函式元件中,我們使用useEffect鉤子函式來處理生命週期內的事件,像下面這樣,
const FunctionalComponent = () => { React.useEffect(() => { console.log("Hello"); }, []); return <h1>Hello, World</h1>;};
useEffect有兩個引數,第一個是箭頭函式,第二個是[],[]裡面是變化的state(s)。什麼意思呢?就是[]中的狀態變化了,箭頭函式會被呼叫。如果像現在這樣寫個[],那箭頭函式只會在元件第一次渲染之後呼叫一次,其功能類似下面類元件的componentDidMount。
class ClassComponent extends React.Component { componentDidMount() { console.log("Hello"); } render() { return <h1>Hello, World</h1>; }}
解除安裝階段
const FunctionalComponent = () => { React.useEffect(() => { return () => { console.log("Bye"); }; }, []); return <h1>Bye, World</h1>;};
這裡注意return的也是一個箭頭函式,這個函式就是在解除安裝階段執行的。當你需要執行一些解除安裝操作,可以放在這裡,比如你可以把clearInterval放在這裡,避免記憶體洩漏。使用useEffect鉤子函式的最大好處就是可以把載入函式和解除安裝函式放在一個同一個地方。這裡對比一下類元件的寫法:
class ClassComponent extends React.Component { componentWillUnmount() { console.log("Bye"); } render() { return <h1>Bye, World</h1>; }}
總結函式元件和類元件各有優缺點,但是函式元件更簡潔,開發更容易;類元件過多的使用this讓整個邏輯看起來很混亂;React團隊主推的React hooks功能也只支援函式元件;React團隊針對函式元件做了更多的最佳化來避免非必要的檢查和記憶體洩漏;React團隊也在努力將hooks功能引入類元件,所以沒必要將現有的類元件都改寫成函式元件;喜歡的老鐵別忘了點波關注,感謝