React Hooks 一步到位

useState

用來聲明狀態變量。

import React, { useState } from 'react';
// ...
const [ count , setCount ] = useState(0);
// ...
  • count 聲明的變量
  • setCount 設用來更新變量的函數
  • 0 初始值
  • 多個狀態聲明不能出現在條件判斷語句中

useEffect

用來代替生命周期函數。

import React, { useEffect } from 'react';

useEffect(()=>{
    // some code
})
  • 第一次組件渲染和每次組件更新都會執行這個函數
  • useEffect中的函數的執行不會阻礙瀏覽器更新視圖,這些函數是異步的

使用 useEffect 實現類似 componentWillUnmount

useEffect(()=>{
    return () => { 
        // some code
    }
})
  • 返回一個函數實現解綁
  • 但是這樣會導致每次狀態發生變化,useEffect 都進行解綁
useEffect(()=>{
    return () => { 
        // some code
    }
}, [])

使用第二個參數,制定哪些狀態發生變化時再解綁

useContext

跨越組件層級直接傳遞變量,實現狀態共享。

  • useContext 解決的是組件之間值傳遞的問題
  • redux 解決的是應用中統一管理狀態的問題
  • useContext 通過和 useReducer 的配合使用,可以實現類似 Redux 的作用

Outer 組件

import React, { createContext } from 'react'
const ValueContext = createContext()
function Outer(){
    return (
        <>
            <ValueContext.Provider value={'我是傳向 Inner 組件的值'}>
                <Inner />
            </ValueContext.Provider>
        </>
    )
}
export default Outer;
  • 使用 createContext 創建 context
  • 使用 createContext 同時生成組件
  • 閉合標簽將組件包裹

Inner 組件

import React, { useContext  } from 'react'
const value = useContext(CountContext)
function Inner(){
    return (
        <>
            <p>{value}</p>
        </>
    )
}
export default Inner;

使用 useContext 來使用上下文

useReducer

用來實現類似 redux 功能

import React, { useReducer } from 'react';

function Demo(){
    const [ count, dispatch ] = useReducer((state,action)=>{
        switch(action){
            case 'add':
                return state+1
            case 'sub':
                return state-1
            default:
                return state
        }
    },0)
    return (
       <>
           <h2>分數:{count}</h2>
           <button onClick={()=>dispatch('add')}>加</button>
           <button onClick={()=>dispatch('sub')}>減</button>
       </>
    )
}

export default Demo
  • state 第一個參數 狀態
  • action 控制業務邏輯的判斷參數

模擬 Redux

  • useContext:可訪問全局狀態,避免一層層的傳遞狀態
  • useReducer:通過action的傳遞,更新復雜邏輯的狀態

顏色共享組件 color.js

import React, { createContext,useReducer } from 'react';

export const ColorContext = createContext({})
export const UPDATE_COLOR = 'UPDATE_COLOR'

const reducer = (state, action) => {
    switch(action.type){
        case UPDATE_COLOR:
            return action.color
        default:
            return state
    }
}

export const Color = props => {
    const [color, dispatch] = useReducer(reducer, 'blue')
    return (
        <ColorContext.Provider value = {{color,dispatch}}>
            {props.children}
        </ColorContext.Provider>
    )
}
  • 用 {props.children} 來顯示子組件
  • 將 color 和 dispatch 共享出去

showArea.js

import React , { useContext } from 'react';
import { ColorContext } from './color';

function ShowArea(){
    const { color } = useContext(ColorContext)
    return (<div style={{ color:color }}>字體顏色為{ color }</div>)
}

export default ShowArea
  • 注意 引入 ColorContext 使用了大括號

Buttons.js

import React , { useContext } from 'react';
import { ColorContext, UPDATE_COLOR } from './color'

function Buttons(){
    const { dispatch } = useContext(ColorContext)
    return (
        <div>
            <button onClick={()=>{dispatch({type:UPDATE_COLOR,color:"red"})}}>紅色</button>
            <button onClick={()=>{dispatch({type:UPDATE_COLOR,color:"yellow"})}}>黃色</button>
        </div>
    )
}

export default Buttons

Demo.js

import React, { useReducer } from 'react';
import ShowArea from './ShowArea';
import Buttons from './Buttons';
import { Color } from './color';   //引入Color組件

function Demo(){
    return (
        <>
            <Color>
                <ShowArea />
                <Buttons />
            </Color>
        </>
    )
}

export default Demo

useMemo

用來解決使用 React hooks 產生的無用渲染的性能問題。

import React , {useState,useMemo} from 'react';

function Demo(){
    const [xiaohong , setXiaohong] = useState('小紅待客狀態')
    const [zhiling , setZhiling] = useState('志玲待客狀態')
    return (
        <>
            <button onClick={()=>{setXiaohong(new Date().getTime())}}>小紅</button>
            <button onClick={()=>{setZhiling(new Date().getTime()+',志玲向我們走來了')}}>志玲</button>
            <ChildComponent name={xiaohong}>{zhiling}</ChildComponent>
        </>
    )
}

function ChildComponent({name,children}){
    function changeXiaohong(name){
        console.log('她來了,她來了。小紅向我們走來了')
        return name+',小紅向我們走來了'
    }

    const actionXiaohong = changeXiaohong(name)
    return (
        <>
            <div>{actionXiaohong}</div>
            <div>{children}</div>
        </>
    )
}

export default Demo

點擊志玲按鈕,小紅對應的方法執行,雖然結果沒變,但是每次都執行,損耗性能。

function ChildComponent({name,children}){
    function changeXiaohong(name){
        console.log('她來了,她來了。小紅向我們走來了')
        return name+',小紅向我們走來了'
    }

    const actionXiaohong = useMemo(()=>changeXiaohong(name),[name]) 
    return (
        <>
            <div>{actionXiaohong}</div>
            <div>{children}</div>
        </>
    )
}

第二個參數 [name] 匹配成功,才會執行。

useRef

  • 用來獲取React JSX中的DOM元素
  • 用來保存變量
import React, { useRef} from 'react';
function Demo(){
    const inputEl = useRef(null)
        inputEl.current.value = "給 input value屬性 賦值"
    return (
        <>
            <input ref={inputEl} type="text"/>
        </>
    )
}

export default Demo
import React, { useRef, useState, useEffect } from 'react'

function Demo(){
    const inputEl = useRef(null)
        inputEl.current.value="給 input value屬性 賦值"

    const [text, setText] = useState('默認值')
    const textRef = useRef()

    useEffect(()=>{
        textRef.current = text
    })

    return (
        <>
            <input ref={inputEl} type="text"/>
            <input value={text} onChange={(e)=>{setText(e.target.value)}} />
        </>
    )
}

export default Demo
  • text 每次發生變化,將值保存到 useRef 中
  • 使用 useEffect 實現每次狀態變化都進行變量重新賦值
  • 很少用到這個功能(保存變量)

自定義 Hooks

編寫自定義函數實現獲取瀏覽器窗口

import React, { useState, useEffect, useCallback } from 'react';

function useWinSize(){
    const [ size , setSize] = useState({
        width:document.documentElement.clientWidth,
        height:document.documentElement.clientHeight
    })

    const onResize = useCallback(()=>{
        setSize({
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight
        })
    },[])

    useEffect(()=>{
        window.addEventListener('resize',onResize)
        return ()=>{
            window.removeEventListener('resize',onResize)
        }
    },[])

    return size;
}

function Demo(){
    const size = useWinSize()
    return (
        <div>頁面Size:{size.width} x {size.height}</div>
    )
}

export default Demo 
  • 命名要使用 use 開頭以確認該函數是自定義 Hook 而不是組件
  • useCallback 用來緩存方法 ( useMemo 是為了緩存變量)
  • 第一次進入方法時用 useEffect 來注冊 resize 監聽事件
  • 防止一直監聽所以在方法移除時,使用return的方式移除監聽
  • 最后在 Demo 組件中使用

文章參考

  • 官方文檔
  • 嗶哩嗶哩up主技術胖的 視頻
posted @ 2020-01-19 10:52  DIVMonster  閱讀(326)  評論(0編輯  收藏
最新chease0ldman老人