React 新特性學習

 

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是在渲染後執行;
  • useMemo函數有返回值,同比 useEffect 沒有返回值;

相同點:

  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

  • 獲取子組件或者DOM節點的句柄
  • 渲染週期之間共享數據的存儲

示例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>
}
相關文章
相關標籤/搜索