如何使用React Hooks將React類元件轉換為功能元件
介紹
React的最新alpha版本引入了一個稱為Hooks的新概念。鉤子被引入到React中以解決常見問題。但是,它們主要用作類的替代方法。使用Hooks,可以建立使用狀態和生命週期方法的功能元件。
鉤子目前在React v16.7.0-alpha中可用。沒有刪除類的計劃。鉤子提供了另一種編寫React的方式。
鑑於Hook仍然是新手,許多開發人員都希望將其概念應用到現有的React應用程式或新應用程式中。在本文中,您將探索使用React Hooks將React類元件轉換為功能元件的五種方法。
先決條件
要完成本教程,您需要:
- 熟悉JavaScript。您可以檢視“如何在JavaScript中編碼”
- 熟悉React。您可以檢視我們的React.js系列中的How to Code指南,以幫助您入門。
不需要本地開發,但提供了CodeSandbox示例以供進一步實驗。
步驟1 —瞭解沒有狀態或生命週期方法的類
讓我們從一個既沒有狀態又沒有生命週期元件的React類開始:
ExampleClassComponent.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;
複製
在這裡,您有一個典型的React類,該類缺少狀態或生命週期方法。單擊按鈕時,它會提醒名稱。
該類的功能等效項如下所示:
ExampleFunctionalComponent.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;
複製
像第一個示例一樣,此功能類的行為也很典型。
但是,此示例未使用Hooks或任何新內容。在這些示例中,您不需要狀態或生命週期。
讓我們看一下帶有狀態的基於類的元件,並學習如何使用Hooks將它們轉換為功能元件。
第2步-將鉤子新增到帶有狀態的類中
讓我們考慮一種情況,您有一個全域性名稱變數,可以在應用程式中從文字輸入欄位更新該變數。
在React中,您可以通過在state
物件中定義name變數並setState()
在我們有一個新值來更新name
變數時呼叫以下方法來處理這種情況:
ExampleClassComponentWithState.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;
複製
當用戶在輸入欄位中輸入名稱並單擊“警報”按鈕時,它將彈出一個警報,其中警報的狀態已定義。
您可以使用Hooks將整個類轉換為功能性的React元件:
ExampleFunctionalComponentWithState.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;
複製
在這裡,您已經介紹了useState
Hook。它允許您在React功能元件中使用狀態。通過useState()
掛鉤,您可以在此功能元件中使用狀態。它使用類似的語法併為陣列分配了解構分配。
考慮這一行:
const [name, setName] = useState('John Doe')
複製
在這裡,name
相當於this.state
普通類元件中的,並且setName
相當於this.setState
。
useState()
Hook中狀態的初始值來自一個引數。換句話說,useState()
引數是狀態的初始值。您可以將其設定為'John Doe'
。這意味著處於狀態的名稱的初始狀態為'John Doe'
。
這段程式碼是一個示例,說明如何使用Hooks將具有狀態的基於類的React元件轉換為功能元件。
讓我們探索其他場景,包括具有多個狀態屬性的類。
步驟3 —將鉤子新增到具有多個狀態屬性的類
您已經研究瞭如何使用轉換一個狀態屬性useState
,但是當您具有多個狀態屬性時,相同的方法將行不通。如果,例如,你有兩個或多個輸入欄位userName
,firstName
以及lastName
,那麼你將有三個狀態特性基於類的成分:
ExampleClassComponentWithMultipleStateProperties.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;
複製
要將此類轉換為帶有Hooks的功能元件,您將不得不採取一些非常規的方法。使用useState()
掛鉤,上面的示例可以寫成:
ExampleFunctionalComponentWithMultipleStateProperties.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;
複製
這是此示例的CodeSandbox。
這演示瞭如何使用useState()
Hook將具有多個狀態屬性的基於類的元件轉換為功能元件。
第4步-將鉤子新增到帶有State和 componentDidMount
讓我們考慮使用state
和的類componentDidMount
。為了進行演示,您將看到一個場景,其中您為三個輸入欄位設定了初始狀態,並在五秒鐘後將它們全部更新為一組不同的值。
為此,您將為輸入欄位宣告一個初始狀態值,並實現一個componentDidMount()
生命週期方法,該方法將在初始渲染之後執行以更新狀態值:
ExampleClassComponentWithStateAndComponentDidMount.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;
複製
當應用執行時,輸入欄位將具有您在狀態物件中定義的初始值。componentDidMount()
五秒鐘後,這些值將更新為您在方法內部定義的值。
接下來,您將使用ReactuseState
和useEffect
Hooks將此類轉換為功能元件:
ExampleFunctionalComponentWithStateAndComponentDidMount.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;
複製
這是此示例的CodeSandbox。
在功能方面,此元件的作用與上一個示例完全相同。唯一的區別是,您沒有使用在類元件中使用的常規state
物件和componentDidMount()
生命週期方法,而是使用了useState
和useEffect
掛鉤。
第5步-新增魚鉤與國家一類,componentDidMount
和componentDidUpdate
接下來,讓我們看一下帶有狀態和兩個生命週期方法的React類:componentDidMount
和componentDidUpdate
。到目前為止,大多數解決方案都使用了useState
Hook。在此示例中,您將專注於useEffect
掛鉤。
為了最好地演示它是如何工作的,讓我們修改程式碼以動態更新<h3>
頁面上的標題。
當前,標題顯示This is a Class Component
。現在,您將定義一種componentDidMount()
方法來更新標頭,使其Welcome to React Hooks
在三秒鐘後顯示:
ExampleClassComponentWithStateAndTwoLifecycleMethods.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;
複製
應用執行時,它將以初始標題開始,This is a Class Component
並Welcome to React Hooks
在三秒鐘後更改為。這是經典componentDidMount()
行為,因為它會在render
函式成功執行後執行。
讓我們新增功能來動態更新另一個輸入欄位中的標題,以便在鍵入時使用新文字更新標題。
為此,您將需要實現componentDidUpdate()
生命週期方法:
ExampleClassComponent.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;
複製
在這裡,你有state
,componentDidMount()
和componentDidUpdate()
。當您執行該應用程式時,該componentDidMount()
功能會將標頭更新為Welcome to React Hooks
三秒鐘。當您開始在標題文字輸入欄位中輸入內容時,<h3>
文字將使用componentDidUpdate()
方法中定義的輸入文字進行更新。
接下來,您將使用useEffect()
Hook將此類轉換為功能元件:
ExampleFunctionalComponentWithStateAndTwoLifecycleMethods.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;
複製
在CodeSandbox上檢視此示例。
使用此元件,您可以通過使用該useEffect()
Hook實現與以前相同的功能。您還優化了程式碼,因為您不必為componentDidMount()
和componentDidUpdate()
函式編寫單獨的程式碼。使用useEffect()
掛鉤,您將獲得兩者的功能。這是因為useEffect()
預設情況下,在初始渲染之後和每個後續更新之後都執行。
第6步-轉換PureComponent
為React memo
React PureComponent的工作方式類似於Component。它們之間的主要區別在於,React.Component
它沒有實現shouldComponentUpdate()
生命週期方法,而React.PureComponent
卻沒有實現。
如果您的應用程式在render()
給定相同的道具和狀態的React.PureComponent
情況下函式呈現的結果相同,則可以在某些情況下提高效能。
React.memo()
以類似的方式工作。當您的功能元件使用相同的道具呈現相同的結果時,可以將其包裝在呼叫中React.memo()
以提高效能。使用PureComponent
和React.memo()
給作出反應的應用效能的顯著增加,因為它降低了在應用中呈現操作的數量。
要了解它們的作用,您將首先檢視元件每兩秒鐘渲染一次的程式碼,無論值或狀態是否發生變化:
ExampleClassComponent.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;
複製
當您執行該應用程式並檢查日誌時,您會注意到它每兩秒鐘渲染一次元件,而狀態或道具沒有任何變化。使用PureComponent
和可以改善這種情況React.memo()
。
大多數時候,您只想在狀態或道具發生變化時重新渲染元件。使用上面的示例,您可以對其進行改進,以PureComponent
使元件僅在狀態或道具發生變化時才重新渲染。
您可以通過匯入PureComponent
和擴充套件它來完成此操作:
ExamplePureComponent.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;
複製
現在,如果再次執行該應用程式,則只會獲得初始渲染。在那之後沒有其他事情發生。這是因為您有class App extends PureComponent {}
而不是class App extends Component {}
。
這解決了不考慮當前狀態而重新渲染元件的問題。但是,如果在setState
方法中實現狀態更改,則會遇到另一個問題。
例如,考慮對的以下更改setState()
:
當前value
設定為1
:
componentDidMount() {
setInterval(() => {
this.setState(() => {
return { value: 1 };
});
}, 2000);
}
複製
讓我們考慮value
設定為的情況Math.random()
:
componentDidMount() {
setInterval(() => {
this.setState(() => {
return { value: Math.round(Math.random()) };
});
}, 2000);
}
複製
在這種情況下,第一個示例元件將在每次值更新為下一個隨機數時重新呈現。但是,PureComponent
僅當狀態或道具發生更改時,才可以重新渲染元件。
現在,您可以探索如何使用React.memo()
以實現相同的修復程式。為此,將元件包裝為React.memo()
:
ExampleReactMemo.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;
複製
這是此示例的CodeSandbox。
這樣可獲得與使用相同的結果PureComponent
。該元件僅在初始渲染之後進行渲染,並且直到狀態或道具發生更改後才重新進行渲染。
結論
在本教程中,您探索了幾種使用React Hooks將現有的基於類的元件轉換為功能元件的方法。
您還查看了將ReactPureComponent
類轉換為的特殊情況React.memo()
。
要在您的應用程式中使用Hooks,請確保將您的React版本更新為支援的版本:
"react": "^16.7.0-alpha",
"react-dom": "^16.7.0-alpha",
複製
現在,您有了一個基礎,可以進一步使用React Hooks進行實驗。