1 contextcss
2 contextTypereact
3 lazywebpack
4 suspenseweb
5 memo編程
6 hooks數組
7 effect hooksapp
===========ide
1 Context svg
提供了一種方式,可以讓數據在組件樹中傳遞而沒必要一級一級手動傳遞 (可是不要濫用,由於它會破壞組件的複用性)函數
API: createContext(defaultValue)
示例1:基本用法
import React,{ Component,createContext } from 'react';//在這裏導出context函數 import logo from './logo.svg'; import './App.css'; const BatteryContext = createContext();//在這裏建立context //孫子組件 class Leaf extends Component { render(){ //在這裏定義 consumer 消費者 return ( <BatteryContext.Consumer> { Battery => <h1>BatteryName:{Battery}</h1> } </BatteryContext.Consumer> ) } } //子組件 class Middle extends Component{ render(){ return <Leaf/>; } } //父組件 class App extends Component { render(){ //在這裏定義 Provider context的提供者 return ( <BatteryContext.Provider value = {60}> <Middle/> </BatteryContext.Provider> ) } } export default App;
示例2: 動態改變其值:
import React,{ Component,createContext } from 'react';//在這裏導出context函數 import logo from './logo.svg'; import './App.css'; const BatteryContext = createContext();//在這裏建立context //孫子組件 class Leaf extends Component { render(){ //在這裏定義 consumer 消費者 return ( <BatteryContext.Consumer> { Battery => <h1>BatteryName:{Battery}</h1> } </BatteryContext.Consumer> ) } } //子組件 class Middle extends Component{ render(){ return <Leaf/>; } } //父組件 class App extends Component { state = { battery:50 } render(){ const {battery} = state; //在這裏定義 Provider context的提供者 return ( <BatteryContext.Provider value = {battery}> <button type="button" onclick={()=>{ this.setState({ battery: battery-1 }) }}/> <Middle/> </BatteryContext.Provider> ) } } export default App;
示例3:多個context嵌套
import React,{ Component,createContext } from 'react';//在這裏導出context函數 import logo from './logo.svg'; import './App.css'; const BatteryContext = createContext();//在這裏建立context const OnlineContext = createContext();//在這裏建立context //孫子組件 class Leaf extends Component { render(){ //在這裏定義 consumer 消費者 return ( <BatteryContext.Consumer> { battery => ( <OnlineContext.Consumer> { online => <h1>battery:{battery},online:{online}</h1> } </OnlineContext.Consumer> ) } </BatteryContext.Consumer> ) } } //子組件 class Middle extends Component{ render(){ return <Leaf/>; } } //父組件 class App extends Component { state = { battery:50, online:false, } render(){ const {battery,online} = state; //在這裏定義 Provider context的提供者 return ( <BatteryContext.Provider value = {battery}> <OnlineContext.Provider value = {online}> <button type="button" onclick={()=>{ this.setState({ battery: battery-1 }) }}> </button> <button type="button" onclick={()=>{ this.setState({ online: !online }) }}/> </button> <Middle/> </OnlineContext.Provider> </BatteryContext.Provider> ) } } export default App;
示例4:使用static 化簡
import React,{ Component,createContext } from 'react';//在這裏導出context函數 import logo from './logo.svg'; import './App.css'; const BatteryContext = createContext();//在這裏建立context const OnlineContext = createContext();//在這裏建立context //孫子組件 class Leaf extends Component { static contextType = BatteryContext; render(){ const battery = this.context; return ( <h1>battery:{battery}</h1> ) } } //子組件 class Middle extends Component{ render(){ return <Leaf/>; } } //父組件 class App extends Component { state = { battery:50, online:false, } render(){ const {battery,online} = state; //在這裏定義 Provider context的提供者 return ( <BatteryContext.Provider value = {battery}> <OnlineContext.Provider value = {online}> <button type="button" onclick={()=>{ this.setState({ battery: battery-1 }) }}> </button> <button type="button" onclick={()=>{ this.setState({ online: !online }) }}/> </button> <Middle/> </OnlineContext.Provider> </BatteryContext.Provider> ) } } export default App;
2 lazy 加載
懶加載組件:
// app.js import React,{ Component ,lazy ,Suspense} from 'react'; //lazy 的返回就是一個react組件 const About = lazy(()=> import('./About.jsx')); //父組件 必須用 Suspense 和lazy進行配合,fallback中加載一個實例 // 或者把 <div>Loading</div> 改成 <Loading/> 組件 class App extends Component { render(){ return ( <div> <Suspense fallback={<div>Loading</div>}> <About/> </Suspense> </div> ) } } export default App;
子組件:
//About.js import React,{ Component } from 'react'; //子組件 export default class About extends Component { render(){ return <div>About</div> } }
從 開發者工具中的 network 中能夠看到:
若是想改變 trunk 的名字,將app父組件改成下面的方式:
// app.js import React,{ Component ,lazy ,Suspense} from 'react'; //lazy 的返回就是一個react組件 const About = lazy(()=> import(/* webpackChunkName:"about"*/'./About.jsx'));
則:
若是子組件加載失敗,增長容錯機制:
// app.js import React,{ Component ,lazy ,Suspense} from 'react'; //lazy 的返回就是一個react組件 const About = lazy(()=> import(/* webpackChunkName:"about"*/'./About.jsx')); // ErrorBoundary 錯誤邊界 // componentDidCatch 生命週期函數 若是 render() 函數拋出錯誤,則會觸發該函數。 // 錯誤在渲染階段中被捕獲,但在事件處理程序中不會被捕獲 class App extends Component { state = { hasError:false }; componentDidCatch(){ this.setState({ hasError:true }) } render(){ if(this.state.hasError){ return <div>error</div> } return ( <div> <Suspense fallback={<div>Loading</div>}> <About/> </Suspense> </div> ) } } export default App;
3。memo
先看一個示例:父組件每次更新state中的 count,其子組件 Foo 都會執行;
// App.jsx import React,{ Component } from 'react'; //子組件 class Foo extends Component { render(){ console.log('foo render'); return null; } } //父組件 class App extends Component { state={ count:0, } render(){ return ( <div> <button onclick={()=>{ this.setState({ count:this.state.count++ }) }}></button> <Foo name="Milk"/> </div> ) } } export default App;
優化一:使用生命週期函數 shouldComponentUpdate:
子組件改成:
//子組件 class Foo extends Component { shouldComponentUpdate(nextProps,nextState){ if(nextProps.name === this.props.name){ return false; } return true; } render(){ console.log('foo render'); return null; } }
優化二: 使用 PureComponent ,子組件改成:
// App.jsx import React,{ Component,PureComponent } from 'react'; //子組件 class Foo extends PureComponent { render(){ console.log('foo render'); return null; } }
可是有侷限性,只能對傳入的屬性作簡單的對比,若是屬性內部發生變化,則不會監聽到,致使子組件不會改動:
// App.jsx import React,{ Component,PureComponent } from 'react'; //子組件 class Foo extends PureComponent { render(){ console.log('foo render'); return <div>{this.props.person.age}</div>; } } //父組件 class App extends Component { state={ count:0, person:{ age:1, } } render(){ const person = this.state.person; return ( <div> <button onclick={()=>{ person.age++ this.setState({ person }) }}></button> <Foo person={person}/> </div> ) } } export default App;
如上所示:改變了父組件中state的 person對象中的age屬性,可是子組件是沒法監聽到的,只能監聽到第一級的數據;
另外一個侷限:
父組件給子組件有個內聯函數: <Foo person={person} cd={()=>{}}/> 的時候,每次改變父組件的state值,都會建立一個新的函數對象。子組件都會被從新渲染;
相似的 <Foo person={person} cd={this.callback.bind(this)}/> ,子組件也會被從新渲染。
改成下面的方法,便可以:
//父組件 class App extends Component { state={ count:0, person:{ age:1, } } callback=()=>{ } render(){ const person = this.state.person; return ( <div> <Foo person={person} cd={this.callback}/> </div> ) } } export default App;
將子組件改成無狀態函數後,每次改變state 子組件也會改變:
// App.jsx import React,{ Component,PureComponent } from 'react'; //子組件 function Foo(props) { render(){ console.log('foo render'); return <div>{props.person.age}</div>; } } //父組件 class App extends Component { state={ count:0, person:{ age:1, } } callback=()=>{ } render(){ const person = this.state.person; return ( <div> <Foo person={person} cd={this.callback}/> </div> ) } } export default App;
可是這樣的話,每次子組件都會被渲染,這時候 memo 出現了:它至關於 class 中的 PureComponent:
// App.jsx import React,{ Component,PureComponent ,memo } from 'react'; //子組件 const Foo = memo( function Foo(props) { render(){ console.log('foo render'); return <div>{props.person.age}</div>; } } ) //父組件 class App extends Component { state={ count:0, person:{ age:1, } } callback=()=>{ } render(){ const person = this.state.person; return ( <div> <Foo person={person} cd={this.callback}/> </div> ) } } export default App;
這樣,拆分的組件傳入的屬性越簡單,越容易寫成上述方式;
6 hooks
類組件不足:
1 狀態邏輯複用難:缺乏複用機制,渲染屬性和高階組件致使層級冗餘;
2 趨向於複雜難以維護: 生命週期函數混雜不相干邏輯,相干邏輯分散在不一樣生命週期
3 this 指向困擾:內聯函數過分建立新句柄,累成員函數不能保證this;
(注意:點擊組件內的按鈕,state中的count發生變化,組件將渲染,但設置默認state只在第一次時渲染)
// react 分爲 聰明組件和傻瓜組件 按個人理解 這個應該用在傻瓜組件中 // 父組件中 state的變化 致使子組件中 props的變化 致使子組件的從新渲染 function App(){ const [count,setCount] = useState(0); const [name,setName] = useState('like'); return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
問題一:useState設置的默認值,如何知道對應的給state?
答: useState自己只是單純的設置一個值,而後是結構賦值功能,賦給了對應的state;
問題二:每一個組件都有useState,它是如何保證只賦值給當前組件中的count,而不是其餘組件的count呢?
答: 利用了js的單線程原理,當前只能賦值給當前做用域下的組件。
問題三: 若是一個組件有多個useState, 每次渲染該組件的時候,是如何返給定義的state 呢?
答:useState是根據第一次渲染執行組件的時候,定義的state順序賦值的,因此不能改變賦值的順序。例以下面的示例:
let id = 0; function App(){ let name,setName; let count,setCount; id++; if(id & 1){//若是id是奇數 [count,setCount] = useState(0); [name,setName] = useState('like'); }else{ [name,setName] = useState('like'); [count,setCount] = useState(0); } return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
下面的形式也是不行的:
let id = 0; function App(){ const [count,setCount] = useState(0); const [name,setName] = useState('like'); id++; if(id>1){ useState('dd'); //從第二次渲染以後 增長的的設置 } return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
下面的形式也不行:
let id = 0; function App(){ const [count,setCount] = useState(0); const [name,setName] = useState('like'); id++; if(id===1){ useState('dd'); //只在第一次渲染時 增長的的設置 } return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
好比說 state的默認值是基於 props 的:注意:點擊組件內的按鈕,state中的count發生變化,組件將渲染,但設置默認state只在第一次時渲染
function App(){ const [count,setCount] = useState(()=>{ return props.defaultCount || 0; //只會執行一次 }); const [name,setName] = useState('like'); return ( <button type="button" onClick = {()=>{setCount(count++)}} > {count} {name} </button> ) }
7 effect hooks 反作用時機
原來的生命週期:
Mount以後: componentDidMount
update 以後 componentDidUpdate
Unmount 以前 componentWillUnmount
用 useEffect 函數來替換;
userEffect函數是在 render以後調用的 ,其功能至關於 componentDidMount/和componentDidUpdate,而且該函數有callback函數,其功能是清除上一次反作用 遺留下來的狀態 至關於componentWillUnmount
示例1: 點擊按鈕 文本中和頁面title均發生變化,使用原來的生命週期開發:
export default class App extends Component { state = { count:0 } componentDidMount(){ doucument.title = this.state.count; } componentDidUpdate(){ doucument.title = this.state.count; } render(){ const { count } = this.state; return ( <button type="button" onClick={()=>{ this.setState({ count:count++ }) }}> {count} </button> ) } }
可見,爲了實現初始化時和數據更新時,title發生變化,一樣的交互代碼要在兩個生命週期中執行兩次,相似的 再加上 監聽函數,須要在卸載生命週期中 去掉卸載函數:
export default class App extends Component { state = { count:0, size:{ with:doucument.doucumentElement.clientWidth, height:doucument.doucumentElement.clientHeight } } componentDidMount(){ doucument.title = this.state.count; window.addEvevntListener('resize',this.onResize,false); } componentwillUnMount(){ window.removeEventListner('resize',this.onResize,false); } onResize = ()=>{ this.setState({ size:{ with:doucument.doucumentElement.clientWidth, height:doucument.doucumentElement.clientHeight } }) } componentDidUpdate(){ doucument.title = this.state.count; } render(){ const { count,size } = this.state; return ( <button type="button" onClick={()=>{ this.setState({ count:count++ }) }}> {count} size:{size.width}X{size.height} </button> ) } }
如今使用 effect hooks:
//1 提升了代碼複用(合併多個生命週期成了一個函數)
//2 優化了關注點分離,即不一樣的事件放在了不一樣的 useEffect 函數中
function App(){ //定義初始化數據 const [count,setCount] = useState(0); const [size,setSize] = useState({ with:doucument.doucumentElement.clientWidth, height:doucument.doucumentElement.clientHeight }); //常規函數 const onResize = ()=>{ setState({ with:doucument.doucumentElement.clientWidth, height:doucument.doucumentElement.clientHeight }) } //1 提升了代碼複用(合併多個生命週期成了一個函數) //2 優化了關注點分離,即不一樣的事件放在了不一樣的 useEffect 函數中 //使用反作用在某些生命週期中執行數據的操做 useEffect(()=>{ doucument.title = count; }) useEffect(()=>{ window.addEvevntListener('resize',onResize,false); return ()=>{ //默認是組件重渲染和組件卸載的時候執行 window.addEvevntListener('resize',onResize,false); } },[]); //上面useEffect函數的空數組的參數,其做用是用於比對。決定該 useEffect 是否執行 // 若是第二個參數不寫,則每次都會執行這個 useEffect ,若是爲空數組,則只執行一次 // 若是數組中寫了數據,則比對每個數據,只有數組中的每一項都不變的狀況下,纔會再次執行; // 以下面,變化size 不會觸發下面useEffect的函數執行 useEffect(()=>{ console.log(count); },[count]) return ( <button type="button" onClick = {()=>{setCount(count++)}}> {count} size:{size.width}X{size:height} </button> ) } export default App;
8 hooks 環境下的的context
由前面 context知識能夠知道 ContextType 只能存在於 Class中,則hook是的無狀態函數咋整?
下面的示例給出了使用context的三個方法:
import React,{ Component, useState, createContext, useContext} from 'react'; const CountContext = createContext();//在這理定義context的外層組件 //子組件(最基礎的寫法) class Foo extends Component{ render(){ return ( <CountContext.Consumer> { count => <h1>{count}</h1> } </CountContext.Consumer> ) } } //子組件(優化的寫法)適用於類組件 class Bar extends Component{ static contextType = CountContext; render(){ const { count} = this.state; return ( <h1>{count}</h1> ) } } //hooks中使用 context 能夠獲取多個 context function Counter(){ const count = useContext(CountContext); return ( <h1>{count}</h1> ) } //父組件 export default class App extends Component { const [ count,setCount] = useState(0); render(){ return ( <div> <button type="button" onClick={()=>{setCount(count+1)}} > click({count})</button> <CountContext.Provider value={count}> <Foo/> <Counter/> </CountContext.Provider> </div> ) } }
9 hooks中的 useMemo 函數
不一樣點:
相同點:
useMemo 函數和 useEffect 函數均有第二個參數,決定是否執行該函數。
示例:
import React,{ Component, useState, useMemo} from 'react'; function Counter(props){ return ( <h1>{props.count}</h1> ) } function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]) return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={count}/> </div> ) } export default App;
如上所示,當 count==3的時候,useMemo中數組的值由 false變爲true, double 發生變化
當 count ==4 的時候, useMemo 中數組的值。由true 變爲 false,double 再次發生變化;
import React,{ Component, useState, useMemo} from 'react'; function Counter(props){ return ( <h1>{props.count}</h1> ) } function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); // 還能夠依賴 memo const half = useMemo(()=>{ return double/4; },[double]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={count}/> </div> ) } export default App;
10 hooks中的 callback 函數
首先看一下memo函數,用memo包裹Counter函數,只有count發生變化的時候,才執行Count函數;
import React,{ Component, useState, useMemo, memo} from 'react'; const Counter = memo(function Counter(props){ cosole.log('count 發生變化的時候才執行'); return ( <h1>{props.count}</h1> ) }) function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={double}/> </div> ) }
這時給 子組件 Counte 增長 回調函數 onclick
import React,{ Component, useState, useMemo, memo} from 'react'; const Counter = memo(function Counter(props){ cosole.log('count 發生變化的時候才執行'); return ( <h1 onClick={props.onClick}>{props.count}</h1> //這裏 ) }) function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); const onClick = ()=>{ console.log('click me'); //父組件中定義回調函數 } return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={double} onClick={onClick}/> //監聽的函數 </div> ) } export default App;
因爲回調函數 onclick的存在,每次父組件中app的變化,都 會致使子組件發生渲染;因此能夠在父組件中使用 memo
function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); const onClick = useMemo(()=>{ return ()=>{ console.log('click me'); } },[]); //改變了這裏 return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={double} onClick={onClick}/> </div> ) }
而後使用 useCallback 化簡:
function App(){ const [count,setCount] = useState(0); const double = useMemo(()=>{ return count*2; },[count === 3]); const onClick = useCallback(()=>{ console.log('click me'); },[]); //改變了這裏 // useMemo(()=>fn); // useCallback(fn); useCallback 至關因而簡化寫法 return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),double:({double}) </button> <Counter count={double} onClick={onClick}/> </div> ) }
這樣,就不會由於父組件中的 回調函數 onClick的變化致使子組件發生變化:
1 子組件中使用 memo函數能夠避免重複渲染,而是根據傳入的props發生變化時才渲染;
2 父組件中使用 useMemo函數能夠避免由於 回調函數的存在,致使子組件的渲染;
11 hooks中的 useRef
示例1:
import React,{ Component, PureComponent,useRef} from 'react';//這裏引入 useRef組件 class Counter extends PureComponent { speak(){ console.log('speak'); } render(){ const { props } = this; return ( <h1 onClick={props.onClick}>{props.count}</h1>
) } } function App(){ const [count,setCount] = useState(0); const counterRef = useRef();//建立一個ref,在組件中使用該counrerRef const onClick = useCallback(()=>{ counterRef.current.speak();//執行子組件中的speak函數,current屬性 獲取最終的值 },[counterRef]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}) </button> <Counter ref={counterRef} count={double} onClick={onClick}/> </div> ) } export default App;
示例2: 假設組件中定義一個變量,每秒加1,要求大於10以後再也不增長;
function App(){ const [count,setCount] = useState(0); const counterRef = useRef(); let it; //由於更新state,會致使app組件從新渲染,it會從新初始化,而state只在第一次初始化,可是也不便於將it //放在state中,畢竟它沒有用於渲染組件 useEffect(()=>{ it = setInterval(()=>{ setCount(count=>count+1) },1000) },[]); useEffect(()=>{ if(count >= 10){ clearInterval(it); } }) const onClick = useCallback(()=>{ counterRef.current.speak(); },[counterRef]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}) </button> <Counter ref={counterRef} count={double} onClick={onClick}/> </div> ) }
使用ref,將it改成相似於類的結構成員變量:
import React,{ Component, PureComponent,useEffect,useRef} from 'react';//這裏引入 useRef組件 class Counter extends PureComponent { speak(){ console.log('speak'); } render(){ const { props } = this; return ( <h1 onClick={props.onClick}>{props.count}</h1> } } function App(){ const [count,setCount] = useState(0); const counterRef = useRef(); let it = useRef();//改變了這裏 useEffect(()=>{ it.current = setInterval(()=>{ //改變了這裏 it.current setCount(count=>count+1) },1000) },[]); useEffect(()=>{ if(count >= 10){ clearInterval(it.current);//改變了這裏 it.current } }) const onClick = useCallback(()=>{ counterRef.current.speak(); },[counterRef]); return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}) </button> <Counter ref={counterRef} count={double} onClick={onClick}/> </div> ) } export default App;
最後:自定義hooks
funciton useCount(defaultCount){ const [count,setCount] = useState(defaultCount); const it = useRef(); useEffect(()=>{ it.current = setInterval(()=>{ setCount(count=>count +1 ); },1000) },[]); useEffect(()=>{ if(count >= 10){ clearInterval(it.current); } }); return [count,setCount] }
示例2 :
import React,{ Component, PureComponent,useEffect,useRef} from 'react';//這裏引入 useRef組件 function useCounter(count) { const size = useSize();//共用 useSize函數1 return ( <h1>{count},{size.width},{size.height}</h1> ) } function useSize(){ const [size,setSize] = useSize({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeigth }) const onResize = useCallback(()=>{ setSize({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeigth }) },[]); useEffect(()=>{ window.addEventListener('resize',onResize,false); return ()=>{ window.removeEventListener('resize',onResize,false); } },[]) } funciton useCount(defaultCount){ const [count,setCount] = useState(defaultCount); const it = useRef(); useEffect(()=>{ it.current = setInterval(()=>{ setCount(count=>count +1 ); },1000) },[]); useEffect(()=>{ if(count >= 10){ clearInterval(it.current); } }); return [count,setCount] } function App(){ const [count,setCount] = useCount(0); //這裏也是自定義的hooks組件 const Counter = useCounter(count); // 這裏調用的是自定義的hooks函數useCounter const size = useSize(); //共用 useSize函數2 return ( <div> <button type="button" onClick={()=>{setCount(count++)}} > click:({count}),<h1>{count},{size.width},{size.height}</h1> </button> {Counter} //這裏調用的 上面 useCounter(count) </div> ) } export default App;
最後注意:
通常hooks 都是由 use 爲前綴的,必定要遵循:
1. 把hook 放在最頂層,不要放在條件語句中,由於它依賴順序;
2. 僅在組件和自定義hooks組件中調用,不要在其餘普通函數中調用,由於普通函數說不清在哪裏會被調用,致使hooks的順序變化,例如
function useLogin (){ //自定義hooks,在其餘地方調用也會是在頂層 有順序的 const [login.setLogin] = useState(); useEffect(()=>{ }) } function fetchNews(){//而普通函數,說不清在哪裏被調用,有肯能致使順序不同 const [pageNo,setPageNo] = useState(); }
===============分割線
Hooks 常見問題:
對傳統react 編程的影響
1 生命週期函數如何映射到hooks
function App(){ useEffect(()=>{ //componentDidMount return ()=>{ //componentWillUnmount } },[]);//第二個參數爲空數組,則只執行一次。掛載時執行一次,卸載時執行一次。 let renderCounter = useRef(0); renderCounter.current++; useEffect(()=>{ if(renderCounter >1){ // componentDidUpdate } }) //沒有第二個參數,則每次都執行 }
2 類實例成員變量如何映射到hooks?
答:使用 useRef
3 Hooks 中如何獲取歷史props和state
function Counter(){ const [count,setCount] = useState(0); const preCountRef = useRef();//useRef不會受組件從新渲染的影響,保留上一次的值,因此定義了ref的值 preCountRef useEffect(()=>{ preCountRef.current = count; //沒有第二個參數,表示每次都執行。則update時將count賦值給ref }) const prevCount = prevCountRef.current; return <h1>now:{count},before:{prevCount}</h1> }
4 如何強制更新一個Hooks組件?
思路:設置一個沒有參與渲染的data,而後改變它的值:
function Counter(){ const [count,setCount] = useState(0); const [updater,setUpdater] = useState(0); function forceUpdate(){ setUpdater(updater => updater+1);//組件沒有用到這個data,強制執行該函數,則更新渲染組件 } const preCountRef = useRef(); useEffect(()=>{ preCountRef.current = count; }) const prevCount = prevCountRef.current; return <h1>now:{count},before:{prevCount}</h1> }