首先 useState 是一個Hook,它容許您將React狀態添加到功能組件javascript
useState 是一個方法,它自己是沒法存儲狀態的css
其次,他運行在 FunctionalComponent 裏面,自己也是沒法保存狀態的html
useState 只接收一個參數 inital value,並看不出有什麼特殊的地方。java
由於類組件有不少的痛點react
bind
,this
指向不明確 好比 常常看到這樣的寫法。// 多是這樣
class MyComponent extends React.Component {
constructor() {
// initiallize
this.handler1 = this.handler1.bind(this)
this.handler2 = this.handler2.bind(this)
this.handler3 = this.handler3.bind(this)
this.handler4 = this.handler4.bind(this)
this.handler5 = this.handler5.bind(this)
// ...more
}
}
// 多是這樣的
export default withStyle(style)(connect(/*something*/)(withRouter(MyComponent)))
複製代碼
開始以前先看一個簡單的例子,在沒有 Hooks 以前咱們是這樣來寫的。ios
import React, {Component} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
Switch: "打開"
};
}
setSwitch = () => {
this.state.Switch === "打開"
? this.setState({ Switch: "關閉" })
: this.setState({ Switch: "打開" });
};
render() {
return (
<div> <p>如今是: {this.state.Switch}狀態</p> <button onClick={this.setSwitch}>Change Me!</button> </div>
);
}
}
export default CommonCollectionPage;
複製代碼
之前函數式組件須要給他本身的狀態的時候咱們老是不得不把函數式組件變成 Class 類組件,如今有了 React Hooks 咱們在也不須要由於一個小狀態而將函數式組件變成類組件,上面的這個例子,就能夠變成下面的這個方法來表現。git
function App() {
const [Switch, setSwitch] = useState("打開");
const newName = () =>
Switch === "打開" ? setSwitch("關閉") : setSwitch("打開");
return (
<div> <p>如今是: {Switch} 狀態</p> <button onClick={newName}>Change Me!</button> </div>
);
}
複製代碼
因此 useState 就是爲了給函數式組件添加一個能夠維護自身狀態的功能。github
動態傳遞參數給 useState 的時候只有第一次纔會生效閉包
經過 useEffect 方法來代替了 class 類組件的 componentDidMount
, componentDidUpdate
, componentWillUnmount
三個組件,那如何一個鉤子怎麼使用纔有 3 個鉤子的不一樣效果呢:
首選咱們先運行起來:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [switch, setSwitch] = useState("打開");
const handleSwitch = _ =>
switch === "打開" ? setSwitch("關閉") : setSwitch("打開");
const [num, setNum] = useState(0);
const add = () => {
setNum(num + 1);
};
const minus = () => {
setNum(num - 1);
};
useEffect(() => {
console.log("改變了狀態");
}, [num]);
return (
<div> <p>如今是: {switch}狀態</p> <button onClick={handleSwitch}>Change Me!</button> <p>數字: {num}</p> <button onClick={add}> +1 </button> <button onClick={minus}> -1 </button> </div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement); 複製代碼
上面這個例子他會再初次渲染的時候打印 改變了狀態 而且每次狀態改變的時候都打印 改變了狀態
那麼這樣的用法就是 componentDidMount
, componentDidUpdate
,的使用
useEffect 方法中加入第二個參數 一個空對象 [ ] 那麼以後只會在首次組件裝載好的時候打印一次
改變狀態了
如今咱們須要當 num 改變狀態的時候 去打印 怎麼辦呢?
剛剛咱們使用了一個空對象 那麼只須要再這個對象中加入咱們須要監聽的狀態那麼他至關於使用了 , componentDidUpdate
, 鉤子函數例如
useEffect(() => {
console.log("改變了狀態");
}, [num]);
複製代碼
那麼如今,當 activeUser 狀態改變的時候咱們會發現又打印出了 改變狀態 這句話。而當 switch 狀態改變的時候並不會打印這句話。
import React, { useState, useEffect } from 'react';
function App() {
useEffect(() => {
console.log('裝載了')
return () => {
console.log('卸載拉');
};
});
return (
<div> xxxx </div>
);
}
export default App;
複製代碼
在 useEffect 中咱們能夠作兩件事情,組件掛載完成時候,還有組件卸載時,只要在 useEffect 中使用閉包,在閉包中作咱們想要在組件卸載時須要作的事就能夠。
首先咱們回顧下之前咱們經常用的類組件,下面是一段實現計數器的代碼:
import React, { Component, useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends Component {
constructor() {
super();
this.state = {
count: 0
};
}
componentDidMount() {
setTimeout(() => {
console.log(`count:${this.state.count}`);
}, 3000);
}
componentDidUpdate() {
setTimeout(() => {
console.log(`count:${this.state.count}`);
}, 3000);
}
render() {
return (
<div> <p>{this.state.count}</p> <button onClick={() => this.setState({ count: this.state.count + 1 }) } > 點擊 3 次 </button> </div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement); 複製代碼
頁面刷新當即,點擊 3 次按鈕,上述這個例子的打印結果會是什麼????
咱們很清楚的瞭解 this.state 和 this.setState 全部咱們會知道打印的是:
一段時間後依此打印 3,3,3,3
不過 hooks 中 useEffect 的運行機制並非這樣運做的。
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
console.log(count);
}, 3000);
});
return (
<div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> 點擊 3 次 </button> </div>
);
}
複製代碼
一段時間後依此打印 0,1,2,3。
其實沒有之前 this.state 使用印象,看到這段代碼的打印結果,會認爲這不是理所固然的嗎?
那咱們想讓上面的類組件,也實現上述 0,1,2,3,4 效果 咱們增長這 2 行代碼
//...
componentDidMount() {
// 新增此行代碼
const newCount = this.state.count;
setTimeout(() => {
console.log(newCount);
}, 3000);
}
componentDidUpdate() {
// 新增此行代碼
const newCount = this.state.count;
setTimeout(() => {
console.log(newCount);
}, 3000);
}
// ....
複製代碼
全部咱們會聯想到 在函數式組件中 取值下面的效果是同樣的
function Counter(props) {
useEffect(() => {
setTimeout(() => {
console.log(props.counter);
}, 3000);
});
// ...
}
複製代碼
function Counter(props) {
const counter = props.counter;
useEffect(() => {
setTimeout(() => {
console.log(counter);
}, 3000);
});
// ...
}
複製代碼
這一點說明了在渲染函數式組件的時候他的更新不會改變渲染範圍內的 props ,state 的值。(表達可能有誤)
固然,有時候咱們會想在effect的回調函數裏讀取最新的值而不是以前的值。就像以前的類組件那樣打印 3.3.3.3。這裏最簡單的實現方法是使用useRef。
首先實現上訴打印 3,3,3,3 的問題。以下代碼所示
function Counter() {
const [count, setCount] = useState(0);
const latestCount = useRef(count);
useEffect(() => {
latestCount.current = count;
setTimeout(() => {
console.log(latestCount.current);
}, 3000);
});
return (
<div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> 點擊 3 次 </button> </div>
);
}
複製代碼
咱們經過 useRef(initVal) 來返回一個可變的 ref 對象,其 current 屬性被初始化爲傳遞的參數 (initVal)。
而後函數式組件沒有生命週期,那咱們怎麼才能獲取 ChartDom 真實的 dom 元素呢?也能夠經過 useRef 實現
import React, { useState, useRef, Fragment, useEffect } from 'react';
import { Button } from 'antd';
function Demo({ count: propsCount = 1 }) {
const [count, setCount] = useState(propsCount);
const refContainer = useRef(null); // 如同以前的 React.createRef();
useEffect(() => {
console.log(refContainer.current, '>>>>>>>>>>>');
});
return (
<Fragment> <Button onClick={() => { setCount(count + 1); }}>Click Me</Button> <p ref={refContainer}>You click {count} times</p> </Fragment>
);
}
export default Demo;
複製代碼
import React, { useReducer, useEffect } from "react";
import ReactDOM from "react-dom";
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
const { count, step } = state;
useEffect(() => {
const id = setInterval(() => {
dispatch({ type: 'tick' });
}, 1000);
return () => clearInterval(id);
}, [dispatch]);
return (
<>
<h1>{count}</h1>
<input value={step} onChange={e => {
dispatch({
type: 'step',
step: Number(e.target.value)
});
}} />
</>
);
}
const initialState = {
count: 0,
step: 1,
};
function reducer(state, action) {
const { count, step } = state;
if (action.type === 'tick') {
return { count: count + step, step };
} else if (action.type === 'step') {
return { count, step: action.step };
} else {
throw new Error();
}
}
複製代碼
在類組件中咱們都是經過使用 ref 的方式 獲取類組件的實例,這樣就可讓父組件調用子組件的方法。
那麼函數式沒有實例,怎麼使用 ref 呢?
// 子組件
import React, { useRef, useImperativeHandle,forwardRef } from "react";
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />; } export default forwardRef(FancyInput); 複製代碼
// 父組件
import React, { useRef } from "react";
function App(){
const fancyInputRef = useRef(null)
// 這樣獲取子組件方法
fancyInputRef.current.focus()
return (
<div> <FancyInput ref={fancyInputRef} /> </div> ) } 複製代碼
全文章,若有錯誤或不嚴謹的地方,請務必給予指正,謝謝!
我的其餘文章推薦:
參考: