靈感來源來自一個面試官問我為什麼hook函數不能在class類組件中使用?如何在class類組件中使用呢?
第一個問題你們自己可以百度一下晚上有,今天着重講解一下第二個問題。
碰到這個問題首先要進行分析:
(1)hook函數在什麼情況下可以調用?答:函數最外層可以調用Hook。
=>可不可以衍生一個函數代替class類組件調用?答:高階組件就可以,因為高階組件本身就是從高階函數演變過來,高階函數我的理解就是在高階函數內調用傳過來的參數(參數一般為回調函數)。高階組件概念差不多,只不過,參數為組件,在react中組件的本質就是函數。。。
接下來,那麼我們現在實現一個需求——————來個數字遞增效果,然後到達某一個值時,停止。
原先錯誤的代碼是這樣的:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="root"></div>
<script src="https://cdn.bootcss.com/react/16.8.6/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.8.6/umd/react-dom.development.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"></script>
<script type="text/babel">
const useState = React.useState;
function useDateHook(){
return new Date().toDateString()
}
let timer = null;
function withHooksHOC(Component){
return function (props){
const nowDate = useDateHook();
let [date,setDate] = useState(5);
timer = setInterval(()=>{
date++;
setDate(date)
/*if(date == 10){ //錯誤1
clearInterval(timer)
}*/
},1000)
if(date == 10){//錯誤2
console.log("exe")
clearInterval(timer)
}
return <Component date={date}
{...props} />;
}
}
class DateCom extends React.Component{
render() {
return <p>date: {this.props.date}</p>;
}
}
let WithHooksHOCer = withHooksHOC(DateCom);
class App extends React.Component{
constructor(props){
super(props)
}
render() {
return <WithHooksHOCer />;
}
};
ReactDOM.render(<App />,document.querySelector('#root'))
</script>
</body>
</html>
錯誤1和錯誤2其實錯誤點都差不多,都是date到達10的時候清掉當前定時器,但是每次HOOK setState函數每次更新數據都會生成一個定時器,慢慢的越來越多,導致頁面崩潰!
在之前科普一下useRef作用?
1.獲取DOM對象
2.保存數據
useRef相當於在函數式組件中添加了一個實例對象。useRef不會因為組件的更新而丟失數據,雖然組件進行了更新,但是通過useRef保存的數據並不隨之發生改變。
3.ref對象的值發生改變之後,不會觸發組件重新渲染。
(https://blog.csdn.net/qq_30267753/article/details/125173931)
下面是解決方案,這裏用到了自定義hook函數,然後借鑑useEffect函數結合高階函數(傳遞函數作為參數)和useRef作用(聲明全局變量)的形式,並配置useEffect第二個參數,數組為空代表,組件掛載後和卸載後執行,[參數]表明,參數變化時,這點剛好契合定時函數定時傳參的效果!即每次調用自定義hook函數的時候初始化生成一個回調函數的鏡像,然後再另外一個副作用函數裏代替執行回調函數內容,相當於直接在自定義hook函數中執行定時器效果。
下面是正確的完整的代碼,可以在瀏覽器跑
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="root"></div>
<script src="https://cdn.bootcss.com/react/16.8.6/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.8.6/umd/react-dom.development.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"></script>
<script type="text/babel">
const useState = React.useState;
const useEffect = React.useEffect;
const useRef = React.useRef;
function useDateHook() {
return new Date().toDateString()
}
function withHooksHOC(Component) {
return function (props) {
const nowDate = useDateHook();
let [date, setDate] = useState(5);
useInterval(() => {
if(date < 10){
setDate(date + 1);
}
}, 1000)
props = {
date,
nowDate
}
return <Component {...props} />
}
}
function useInterval(callback,delay){
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback; //每次都初始化一個saveCallBack對象 用於後面副作用函數執行。
}, [callback]); //第二個參數代表callback每次變化就就行,數組為空就代表組件掛載和卸載的時候執行
useEffect(() => {
let id = setInterval(() => {
savedCallback.current(); //執行回調
}, delay);
return function (){
clearInterval(id);
}
}, [delay])
}
class DateCom extends React.Component {
render() {
return <div id="box"><p>date: {this.props.date}</p><p>props:{this.props.nowDate}</p></div>
}
}
let WithHooksHOCer = withHooksHOC(DateCom);
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return <WithHooksHOCer />;
}
};
ReactDOM.render(<App />, document.querySelector('#root'))
</script>
</body>
</html>
參考文獻:
https://www.52dianzi.com/category/article/37/689191.html
。
https://baijiahao.baidu.com/s?id=1744203108622010543&wfr=spid...
。