react新特性hooks使用詳細教程

Hook 是 React 16.8 的新增特性。它可讓你在不編寫 class 的狀況下使用 state 以及其餘的 React 特性。html

爲何使用hooks?

  • 在組件之間複用狀態邏輯很難,可能要用到render props和高階組件,React 須要爲共享狀態邏輯提供更好的原生途徑,Hook 使你在無需修改組件結構的狀況下複用狀態邏輯
  • 複雜組件變得難以理解,Hook 將組件中相互關聯的部分拆分紅更小的函數(好比設置訂閱或請求數據)
  • 難以理解的 class,包括難以捉摸的this

使用hooks注意點

  • 只能在函數最外層調用 Hook。不要在循環、條件判斷或者子函數中調用。
  • 只能在 React 的函數組件中調用 Hook。不要在其餘 JavaScript 函數中調用

更詳細的瞭解能夠看下官網react

1.useState使用

//組件函數每次渲染都會被調用,每次執行都產生一個獨立的閉包
import React,{useState,useEffect} from 'react';
import {render} from 'react-dom';
function Home(){
    let [count1,setCount1] = useState(0);
    function alertCount1(){
        setTimeout(()=>{
          alert(count1);
        },3000);
      }
    return <div>
        <p>count1: {count1}</p>
        <button onClick={()=>setCount1(count1+1)}>count1+</button>
        <button onClick={alertCount1}>alertCount1</button>
    </div>
}

render(<Home />,window.root);

複製代碼

2.useEffect使用

  • 不須要清除的反作用
import React,{useState,useEffect} from 'react';
import {render} from 'react-dom';
function Home(){
    let [count1,setCount1] = useState(0);
    //若是你熟悉 React class 的生命週期函數,你能夠把 useEffect Hook 看作 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。
    useEffect(()=>{
        document.title = `這事第${count1}點擊`;
    })
    return <div>
        <p>count1: {count1}</p>
        <button onClick={()=>setCount1(count1+1)}>count1+</button>
    </div>
}

render(<Home />,window.root);

複製代碼
  • 須要清除的反作用

下面每次count1加1都沒從新開啓一個新的定時器因此就會越加越快, 此次須要清除反作用。useEffect能夠返回一個函數,該函數會在組件卸載或者從新re-render前執行。算法

缺點是: 連續點擊count1加時,就會屢次建立定時器和清除定時器。也不會打印11111。redux

import React,{useState,useEffect} from 'react';
import {render} from 'react-dom';
function Home(){
    let [count1,setCount1] = useState(0);
    //若是你熟悉 React class 的生命週期函數,你能夠把 useEffect Hook 看作 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。
    useEffect(()=>{
       let timer = setInterval(()=>{
           console.log(11111);
       },1000);
       return ()=>{
          clearInterval(timer);
       }
    })
    return <div>
        <p>count1: {count1}</p>
        <button onClick={()=>setCount1(count1+1)}>count1+</button>
    </div>
}

render(<Home />,window.root);
複製代碼

useEffect第二個參數是依賴項。若是設置空數組則不依賴任何參數。因此re-render時候就不會執行useEffect函數。此時useEffect返回函數只會在組件卸載的時候執行。數組

import React,{useState,useEffect} from 'react';
import {render} from 'react-dom';
function Count1(){
    let [count1,setCount1] = useState(0);
    //若是你熟悉 React class 的生命週期函數,你能夠把 useEffect Hook 看作 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。
    useEffect(()=>{
       let timer = setInterval(()=>{
           console.log(11111);
       },1000);
       return ()=>{
          clearInterval(timer);
       }
    },[])
    return <div>
        <p>count1: {count1}</p>
        <button onClick={()=>setCount1(count1+1)}>count1+</button>
    </div>
}

function Home(){
    let [isShow,setIsShow] = useState(false);
    //若是你熟悉 React class 的生命週期函數,你能夠把 useEffect Hook 看作 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。
    let changeShow = ()=>{
        setIsShow(!isShow);
    }
    return <div>
        <button onClick={changeShow}>show</button>
        {isShow&&<Count1></Count1>}
        
    </div>
}

render(<Home />,window.root);
複製代碼

3.useReducer使用

  • useState 的替代方案。它接收一個形如 (state, action) => newState 的 reducer,並返回當前的 state 以及與其配套的 dispatch 方法
  • 在某些場景下,useReducer 會比 useState 更適用,例如 state 邏輯較複雜且包含多個子值,或者下一個 state 依賴於以前的 state 等
import React,{useState,useEffect,useReducer} from 'react';
import {render} from 'react-dom';

let initCount = 1;
function countRender(state,action){
    switch(action.type){
        case 'ADD':
            return state+action.num;
        case 'REDUCE':
            return state-action.num;
        default:
            return  state;     
    }
}


function Home(){
    //這裏熟悉redux用法的會有種親切的趕腳
    //useReducer 三個參數 
    //第一個reducer
    //第二個initialState 第三個參數的時候直接做爲初始化數據,有第三個參數的時候做爲第三個參數函數的入參。
    //第三個 initializer 惰性初始化函數返回初始化數據 
    let [count1,dispatch] = useReducer(countRender,initCount,(state)=>10+state);
    return <div>
        <p>count1: {count1}</p>
        <button onClick={()=>dispatch({
            type:"ADD",
            num:3
        })}>ADD</button>
         <button onClick={()=>dispatch({
            type:"REDUCE",
            num:2
        })}>REDUCE</button>
    </div>
}


render(<Home />,window.root);
複製代碼

4.useRef使用

  • 組件內使用
function Home(){
    let myinput = useRef();
    let submit = ()=>{
        console.log(myinput.current.value);
    }
    return <div>

        <input ref={myinput} type="text"/>
        <button  onClick={submit}>提交</button>
    </div>
}
複製代碼
  • 父子組件轉發
import React,{useState,useEffect,useRef,useImperativeHandle} from 'react';
import {render} from 'react-dom';

function Home(){
    let myinput1 = useRef();
    let myinput2 = useRef();
    let setInputs1 = ()=>{
        myinput1.current.setValue("hello world");
        myinput1.current.focus();
    }
    let setInputs2 = ()=>{
        myinput2.current.value = "hello";
    }
    return <div>
        <button  onClick={setInputs1}>設置1</button>
        <WithRefMyInputs1 ref={myinput1}/>
        <button  onClick={setInputs2}>設置2</button>
        <WithRefMyInputs2 ref={myinput2}/>
    </div>
}

function MyInputs1(props,ref){
    let focusInput = useRef();
    let valInput = useRef();
    //自組件能夠只暴露部分操做。
    useImperativeHandle(ref,()=>({
        focus(){
            focusInput.current.focus();
        },
        setValue(val){
            valInput.current.value =  val;
        }
    }))
    return <div>
        <div>
            獲取焦點: <input ref={focusInput}/>
        </div>
        <div>
            設置初始值: <input ref={valInput}/>
        </div>
    </div>
}
function MyInputs2(props,ref){
    //子組件功能所有暴露
    return <div>
        <div>
            設置初始值: <input ref={ref}/>
        </div>
    </div>
}
let WithRefMyInputs1 = React.forwardRef(MyInputs1);
let WithRefMyInputs2 = React.forwardRef(MyInputs2);
render(<Home />,window.root);
複製代碼

5.useLayoutEffect使用

  • 其函數簽名與 useEffect 相同,但它會在全部的 DOM 變動以後同步調用 effect
  • 可使用它來讀取 DOM 佈局並同步觸發重渲染、
  • 在瀏覽器執行繪製以前useLayoutEffect內部的更新計劃將被同步刷新
  • 儘量使用標準的 useEffect 以免阻塞視圖更新
import React,{useState,useEffect,useLayoutEffect} from 'react';
import {render} from 'react-dom';
function LayoutEffect() {
    const [color, setColor] = useState('red');
    //用法同useEffect 只是執行時機不一樣
    useLayoutEffect(() => {
        alert(color);
    });
    useEffect(() => {
        console.log('color', color);
    });
    return (
        <>
            <div style={{ background: color }}>顏色</div>
            <button onClick={() => setColor('red')}>紅</button>
            <button onClick={() => setColor('yellow')}>黃</button>
            <button onClick={() => setColor('blue')}>藍</button>
        </>
    );
}
render(<LayoutEffect />,window.root);
複製代碼

6.自定義hook

  • 經過自定義 Hook,能夠將組件邏輯提取到可重用的函數中。
//實現多個不一樣時間的倒計時功能
import React,{useState,useEffect,useLayoutEffect} from 'react';
import {render} from 'react-dom';

function formate(diffs){
    let h  = Math.floor(diffs/3600); 
    let m = Math.floor((diffs%3600)/60);
    let s = diffs%60;
    let zero =n=> n<10?'0'+n:n;
    return `${zero(h)}:${zero(m)}:${zero(s)}`;
}
function diffTime(futureDate){
    let futureTime = new Date(futureDate).valueOf();
    let time = new Date().valueOf();
    let diffs = (futureTime-time)/1000;
    return diffs;
}
function useDiffTime(futureDate){
    let [time,setTime] = useState(diffTime(futureDate));
    useEffect(()=>{
        setInterval(()=>{
            setTime(time=>time-1);
        },1000)
    },[])
    return time;
}
function Timer1(){
    let time= useDiffTime(new Date().valueOf()+4200000);; 
    return <div>
        倒計時{formate(time)}
    </div>
}
function Timer2(){
    let time= useDiffTime(new Date().valueOf()+3600000);; 
    return <div>
        倒計時{formate(time)}
    </div>
}
function Home(){
    return <div>
        <Timer1></Timer1>
        <Timer2></Timer2>
    </div>
}

render(<Home />,window.root);
複製代碼

7.性能優化

  • 調用 State Hook 的更新函數並傳入當前的 state 時,React 將跳過子組件的渲染及 effect 的執行。(React 使用 Object.is 比較算法 來比較 state。)簡單不在給出事例。
  • 減小渲染次數。

1)把內聯回調函數及依賴項數組做爲參數傳入 useCallback,它將返回該回調函數的 memoized 版本,該回調函數僅在某個依賴項改變時纔會更新。瀏覽器

2)把建立函數和依賴項數組做爲參數傳入 useMemo,它僅會在某個依賴項改變時才從新計算 memoized 值。這種優化有助於避免在每次渲染時都進行高開銷的計算性能優化

import React,{useState,useCallback,useMemo,memo} from 'react';
import {render} from 'react-dom';

function Child({onButtonClick,data}){
    console.log('Child render');
    return (
        <button onClick={onButtonClick} >{data.number}</button>
    )
}
let MemoChild = memo(Child);
function App(){
    const [number,setNumber] = useState(0);
    const [name,setName] = useState('zhufeng');
    const addClick = useCallback(()=>setNumber(number+1),[number]);
    const  data = useMemo(()=>({number}),[number]);
    console.log("App render");
    return (
      <div>
        <input type="text" value={name} onChange={e=>setName(e.target.value)}/>
        <MemoChild onButtonClick={addClick} data={data}/>
      </div>
    )
}

render(<App />,window.root);
複製代碼

參考文獻:bash

相關文章
相關標籤/搜索