聊聊函數(shù)式組件與類(lèi)組件有何不同
前言
React 中最關(guān)鍵的知識(shí)點(diǎn)就是組件,在 React 16.8 之前(還沒(méi)有 Hooks 前),我們的應(yīng)用大多寫(xiě)成 Class 組件,因?yàn)?Class 組件有生命周期,能控制狀態(tài)(state)。但函數(shù)式組件只能默默站在后面,說(shuō)自己是木偶組件(也叫無(wú)狀態(tài)組件),傳來(lái) props,展示UI
以下文字都基于有了 Hooks 后
正文
函數(shù)式組件和類(lèi)組件之間是否有什么根本上的區(qū)別?
函數(shù)式組件捕獲渲染時(shí)的值
具體可以看這篇文章:函數(shù)式組件與類(lèi)組件有何不同?
因?yàn)樵?React 中 props 是不可變(immutable)的,它們永遠(yuǎn)不會(huì)改變。然而,this 是可變(mutable)的
事實(shí)上,這就是類(lèi)組件 this 存在的意義。React 本身會(huì)隨著時(shí)間的推移而改變,以便你可以在渲染方法以及生命周期方法中得到最新的實(shí)例
函數(shù)式組件會(huì)捕獲當(dāng)前狀態(tài)下的值,如果你使用定時(shí)器改變當(dāng)前值的狀態(tài),那函數(shù)式組件顯示的還是原來(lái)的值,而不是最新值。而類(lèi)組件會(huì)一直獲取最新值
只要一渲染,函數(shù)式組件就會(huì)捕獲當(dāng)前的值。而類(lèi)組件即使渲染了,但是它的 this 會(huì)指向最新的實(shí)例
類(lèi)組件
可以看線(xiàn)上Demo
class ClassDemo extends React.Component {
state = {
value: ""
};
showMessage = () => {
alert("最新值為 " + this.state.value);
};
handleMessageChange = (e) => {
this.setState({ value: e.target.value });
};
handleClick = () => {
setTimeout(this.showMessage, 3000);
};
render() {
return (
<div>
<input value={this.state.value} onChange={this.handleMessageChange} />
<button onClick={this.handleClick}>點(diǎn)擊</button>
</div>
);
}
}
這樣的結(jié)果是點(diǎn)擊后獲取到最新的值,而不是 3 秒前的值。為什么?因?yàn)?this 可變,3 秒之后執(zhí)行 alert("最新值為 " + this.state.value)。 this.state.value 指向最新的值
如果類(lèi)組件如果想保存原來(lái)的值該怎么做?
一、調(diào)用事件之前讀取this.props
可以看線(xiàn)上Demo
showMessage = (value) => {
alert("最新值為 " + value);
};
handleClick = () => {
const { value } = this.state;
setTimeout(() => this.showMessage(value), 3000);
};
可以解決,但點(diǎn)擊時(shí)獲取到當(dāng)前的 user,再傳遞給 this.showMessage,這樣,即使 3 秒之后也是原來(lái)的值
缺點(diǎn):每次都要從 this.props 中拿值,如果數(shù)據(jù)一多,寫(xiě)起來(lái)不符合人性
二、在構(gòu)造函數(shù)中綁定方法
可以看線(xiàn)上Demo
constructor(props) {
super(props);
this.showMessage = this.showMessage.bind(this);
this.handleClick = this.handleClick.bind(this);
}
這個(gè)方法解決不了問(wèn)題。我們的問(wèn)題是我們從 this.props 中讀取數(shù)據(jù)太遲了—— 讀取時(shí)已經(jīng)不是我們所需要使用的上下文
三、利用閉包
把方法寫(xiě)進(jìn) render 中,這樣每次渲染時(shí)就能捕獲住當(dāng)時(shí)所用的 props 或者 state
可以看線(xiàn)上Demo
class ClassDemo extends React.Component {
state = {
value: ""
};
render() {
const { value } = this.state;
const showMessage = () => {
alert("最新值為 " + value);
};
const handleMessageChange = (e) => {
this.setState({ value: e.target.value });
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<div>
<input value={this.state.value} onChange={handleMessageChange} />
<button onClick={handleClick}>點(diǎn)擊</button>
</div>
);
}
}
但是這個(gè)方法很蠢,這個(gè)寫(xiě)法和函數(shù)式組件有什么區(qū)別呢?還不如用函數(shù)式組件呢
函數(shù)式組件如果想保存最新的值呢
使用 useRef 保存最新的值,讓組件獲得最新的值
function MyComponent() {
const ref = useRef(null);
}
首先,ref 與實(shí)例都扮演同樣的角色,ref 對(duì)象是一個(gè)有 current 屬性的一個(gè)容器
上次的例子我們用函數(shù)式組件就可以這樣寫(xiě):
const FunctionDemo = () => {
const [value, setValue] = useState("");
const refValue = useRef("");
const showMessage = () => {
alert("最新值為 " + refValue.current);
};
const handleMessageChange = (e) => {
setValue(e.target.value);
refValue.current = e.target.value;
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<div>
<input value={value} onChange={handleMessageChange} />
<button onClick={handleClick}>點(diǎn)擊</button>
</div>
);
};
可以看線(xiàn)上Demo
這里筆者提出兩個(gè)疑問(wèn):
- 為什么 ref 能保存住最新的值?
- 為什么函數(shù)式組件會(huì)捕獲,類(lèi)組件不會(huì)呢?