react hooks实用笔记

125 阅读5分钟

React-Hooks

一. useState

useState基本使用方式

  • const [count,setCount] = useState(0)
  • count:我们定义的状态
  • setCount:操作状态的方法 Function
  • useState(0):函数,参数是我们给状态的初始值
//定义一个函数式组件
import React, { useState } from "react"

export default function FuncCom() {
    /* 
    引入useState 
    数组解构 
        1. 第一个值是我们定义的参数
        2. 第二个值是操作参数的方法 使用方式:		setCount(count+1)
        3. useState(0) 定义count初始值为0
    
    */
    const [count, setCount] = useState(0)
    return (<div>
        <h2>function Component {count}
            <button onClick={() => { setCount(count + 1) }}>count+1</button>
        </h2>
    </div>)
}

二. useEffect

作用:代替生命周期函数 componentDidMount componentDidUpdate componentWillUnmount

  • useEffect在dom被创建、更新、销毁的时候调用

  • 结构 useEffect(*Function*,*Array*)

  • useEffect可以代替componentDidMount componentWillUnmount componentDidUpdate

  • useEffect中第一个参数为函数,第二个参数是一个数组

useEffect(()=>{
    //函数体
    return ()=>{
        //返回函数
    }
},[])
  • 函数中的函数体是在组件创建和更新时调用,返回值函数在组件被销毁时调用

  • 数组中的元素对函数的调用做约束,数组中的元素发生改变,函数被调用,数组为空则不调用

import React, { useState, useEffect } from "react"

import { BrowserRouter, Route, Link, Switch } from "react-router-dom"

function Page1() {
    useEffect(() => {
        console.log('page1组件被创建或者更新')
        return () => {
            //返回的函数在组件被销毁时执行
            console.log('page1组件被销毁')
        }
    }, [])
    return (
        <h3>page1</h3>
    )
}
function Page2() {
    useEffect(() => {
        console.log('page2组件被创建或者更新')
    }, [])
    return (
        <h3>page2</h3>
    )
}

export default function FuncDemo() {
    //useState用于创建组件状态
    const [count, countAdd] = useState(0)
    const handleCount = () => countAdd(count + 1)
    useEffect(() => {
        //函数体在组件更新和创建时调用
        console.log('dom has been update')
        return () => {
            //返回的函数在组件被销毁时执行
            console.log('组件被销毁')
        }
    }, [count])
    return (
        <BrowserRouter>
            <div>
                {count}&nbsp;&nbsp;&nbsp;
                <button onClick={handleCount}>count+</button>
                --------------------------------------
                <ul>
                    <li><Link to="/page1">page1</Link></li>
                    <li><Link to="/page2">page2</Link></li>
                </ul>
                <Switch>
                    <Route path="/page1" component={Page1}></Route>
                    <Route path="/page2" component={Page2}></Route>
                </Switch>
            </div>
        </BrowserRouter>
    )
}

三. useContext

  1. 引入createContext useContext

  2. 创建上下文组件 const CountContext = createContext()

  3. 子组件内获取状态 let count = useContext(CountContext)

  4. 父组件提供数据

    <CountContext.Provider value={count}>
    	<Counter/> //子组件名
    </CountContext.Provider>
    

使用实例

import React, { useState, createContext, useContext } from "react"

const CountContext = createContext()


// 子组件
function ShowCount() {
    //使用useContext接收CountContext内容
    let count = useContext(CountContext)
    return (
        <h2>{count}</h2>
    )
}

//父组件
export default function UseContextInsteadOfProps() {
    const [count, setCount] = useState(0)
    return (
        <div>
            <h1>UseContextInsteadOfProps</h1>
            <h1>{count}</h1>&nbsp;
            <button onClick={() => { setCount(count + 1) }}>setCount</button>

            {/* 使用前面创建的CountContext组件传参 */}
            <CountContext.Provider value={count}>
                {/* 在CountContext内显示子组件 */}
                <ShowCount />
            </CountContext.Provider>
        </div>
    )
}

四. useReducer

  1. useReducer基本结构

    //定义
    //count:我们监听的状态
    //dispatch:分发事件的函数
    //state:接收到count的形参
    //action:调用dispatch时传递的参数
    const [count,dispatch] = useReducer((state,action)=>{
        switch(action){
            case 'per1':
                return 'opration1',
            case 'per2':
            	return 'opration2'
            ...
            default:
            	return initstate
        }
    },initState)
    //使用
    ()=>dispatch('per1')
    
  2. 实例

import { ReactElement, useReducer } from "react";

//定义初始化变量
let initialCount: number = 0;
//定义reducer函数 和redux一样,两个参数,state和action
const count_reducer = (
    state: number,
    action: { type: string, data: number }
): number => {
    //数据处理
    const { type, data } = action;
    switch (type) {
        case "add":
            return state + data;
        case "sub":
            return state - data;
        default:
            return initialCount;
    }
}

const LearnReducer: React.FC = (): ReactElement => {
    //useReducer进行状态管理
    const [count,dispatch] = useReducer(count_reducer,initialCount)
    return (
        <>
            <h1>{count}</h1>
            {/*使用dispatch分发状态管理事件*/}
            <button onClick={() => dispatch({ type: "add", data: 5 })}>add5</button>
            <button onClick={() => dispatch({ type: "sub", data: 5 })}>sub5</button>
            <button onClick={() => dispatch({ type: "",data:3})}>reset</button>
        </>
    )
}

export default LearnReducer

五. useMemo

由于默认情况下,父组件状态改变后,子组件也会重新渲染,这样会导致严重的效率问题,让原本没必要重新渲染的组件被重新渲染

实例:

import React, { useState, useMemo } from "react"

export default function LearnUseMemo() {
    //定义两个状态
    const [m, setm] = useState(0)
    const [n, setn] = useState(10)

    return (
        <div>
            {/* 将状态传递给子组件 */}
            < Child1 m={m} />
            < Child2 n={n} />

            {/* 点击按钮改变状态 */}
            <button onClick={() => setm(m + 1)}>m++</button>
            <button onClick={() => setn(n + 1)}>n++</button>
        </div>
    )
}

const Child1 = props => <h2>{props.m}</h2>

//只给Child2组件添加调用时打印内容
const Child2 = props => {
    console.log('Child2被调用')
    return <h2>{props.n}</h2>
}
  • 一般看来,上面的组件是没有任何问题的
  • 但是上面的组件有严重的效率问题

describe:

  1. 创建一个父组件LearnUseMemo
  2. 创建两个状态m,n
  3. 创建两个子组件Child1 Child2 分别将m,n传递给子组件

problem:

点击n会打印内容,没有问题,因为打印内容在子组件child2内

但是点击m也会打印内容,这是没有必要的,因为m状态的更新和child2组件的渲染没有任何关系,这是因为子组件更新默认重新渲染的是父组件,useMemo就是用来解决这种情况带来的问题

使用useMemo

  1. useMemo(Callback,Array)

  2. 更改状态时将方法设置为useMemo的内部参数

  3. Array的作用和useEffect一样

  4. 子组件最外层由React.memo()包裹

    const Child1 = React.memo(props => <h2>{props.m}</h2>)
    

六 useCallback

useCallback是useMemo的语法糖,简化了useMemo内部回调混乱的问题

以上代码修改后

import React, { useState, useMemo, useCallback } from "react"

export default function LearnUseMemo() {
    //定义两个状态
    const [m, setm] = useState(0)
    const [n, setn] = useState(10)

    const addm = useMemo(() => {
        return () => {
            setm(m + 1)
        }
    }, [m])
    //useCallback是useMemo的语法糖,简化了useMemo写法
    const addn = useCallback(() => {
        setn(n + 1)
    }, [n])

    return (
        <div>
            {/* 将状态传递给子组件 */}
            < Child1 m={m} />
            < Child2 n={n} />

            {/* 点击按钮改变状态 () => setm(m + 1)*/}
            <button onClick={addm}>m++</button>
            <button onClick={addn}>n++</button>
        </div>
    )
}

const Child1 = React.memo(props => <h2>{props.m}</h2>)

//只给Child2组件添加调用时打印内容
const Child2 = React.memo(props => {
    console.log('Child2被调用')
    return <h2>{props.n}</h2>
})

七. useRef

调用useRef方法会创建一个对象,对象中只有一个属性current

将一个属性保存在内,它的地址一直不会变。

保存变量(少用)

调用方式:const c = useRef(0)

打印 c: {current:0}

import React, { useRef, useEffect, useState } from 'react'

const LearnUseRef_02 = () => {
    //调用useRef储存状态
    let r = useRef(0)
    const [flag, setFlag] = useState()
    const changeR = () => {
        //直接在这里更改不会被渲染到dom上
        r.current++
        setFlag(r.current)
        console.log('r.current:', r.current)
    }
    useEffect(() => {
        console.log('flag:', flag);
    })
    return (
        <div>
            <h1>r.current:{r.current}</h1>
            <button onClick={changeR}>changeR</button>
        </div>
    )
}

export default LearnUseRef_02

储存dom元素

调用方式:

1. const inputElement = useRef(null)
2. <input ref={inputElement}/>
3. alert(inputElement.current.value)

实例

//useRef挂载dom元素
import React, { useRef } from "react"

const LearnUseRef = () => {
    //通过useRef初始化一个变量挂载dom节点
    const inputEl = useRef(null)
    const showInputData = () => {
        //通过inputEl.current来获取dom节点
        alert(inputEl.current.value)
        console.log(inputEl) //{current: input}
    }
    return (
        <div>
            <input type="text" ref={inputEl} />
            <button onClick={showInputData}>显示内容</button>
        </div>
    )
}
export default LearnUseRef

自定义hooks函数

//自定义hooks函数
import React, { useState, useEffect, useCallback } from "react";

function useWinSize() {
    const [size, setSize] = useState({
        width: document.body.clientWidth,
        height: document.body.clientHeight
    })
    const onResize = useCallback(() => {
        setSize({
            width: document.body.clientWidth,
            height: document.body.clientHeight
        })
    })
    useEffect(() => {
        window.addEventListener('resize', onResize)
        return () => {
            window.removeEventListener('resize', onResize)
        }
    })
    return size
}

const DefinedHooks = () => {
    const size = useWinSize()
    console.log(size);
    return (
        <div>
            <h1>宽:{size.width}  高:{size.height}</h1>
        </div>
    )
}

export default DefinedHooks