React + 项目(从基础到实战) -- 第三期

时间:2024-04-04 15:00:36

react内置hooks

useState

如何让页面动起来(实时更新)

import React,{FC,useState} from "react";

  

const Demo:FC=()=>{

   let count=0; //普通js变量无法触发组件更新

  

  

   function add(){

    count++;

   
    console.log("count: ",count);

   }

    return <div>

        <button onClick={add}>add {count}</button>

    </div>

}

  
  

export default Demo;

在这里插入图片描述

为什么采用

state的改变可以触发函数组件的更新
(如果js变量不在jsx中使用,别用useState)

import React,{FC,useState} from "react";

  

const Demo:FC=()=>{

//    let count=0; //普通js变量无法触发组件更新

   const[count,setCount]=useState(0); //useState 可以触发组件更新

  

   function add(){

    // count++;

    setCount(count+1)

    console.log("count: ",count);

   }

    return <div>

        <button onClick={add}>add {count}</button>

    </div>

}

  
  

export default Demo;

在这里插入图片描述

特点

  1. 不可变数据
    不修改原数据,而是传入新的值

  2. 异步更新|
    函数中无法直接获取最新的值
    在这里插入图片描述

  3. 可能被合并
    使用函数state可以解决这个问题

在这里插入图片描述

 setCount(()=>{

        return count+5

    })

immer

是一个插件
解决state不可变数据的影响
安装 npm install -D immer

 import {FC,useState} from "react";

import {produce} from "immer";

  

const Demo : FC =()=>{

  

    const [list,setList]=useState(['x','y'])

    function add(){

        // setList(list.concat('z'));

        setList(

            produce(draft=>{

                draft.push('z')//直接在原数组上修改

            })

        )

  

    }

  

    return (

        <div>

        <h2>state 不可变数据</h2>

        <div>{JSON.stringify(list)}</div>

        <button onClick={add}>add  item</button>

  
  

        </div>

    )

}

  

export default  Demo;

useEffect

为什么

组件是一个函数,返回的是JSX片段,
组件初次渲染完成,即函数执行完成,
一般情况下函数执行完成就结束了
但是在state更新时,会触发组件更新,函数组件再次执行

为了解决以上问题,使用useEffect

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

const Demo:FC=()=>{

    useEffect(()=>{

        console.log("组件初次渲染完成");

  

        return ()=>{

            console.log("组件销毁时执行");

        }

    },[])//无依赖项,只执行一次

  

    const[count,setCount]=useState(0); //useState 可以触发组件更新

  

   function add(){

    // count++;

    setCount(()=>{

        return count+5

    })

    console.log("count: ",count);

   }

    return <div>

        <button onClick={add}>add {count}</button>

    </div>

  

}

  
  

export default Demo;

注意

发现useEffect 执行两次

在react 18 中,useEffect 在开发环境中执行两次,在生产环境中执行一次
(模拟组件生命周期,以便尽早暴露问题)
开发环境 npm run build 生存环境

useRef

  1. 操作DOM
  2. 可传入普通js变量,但是更新不会触发rerender

与vue3 ref 不同

  

import { FC,useRef } from "react";

  

const Demo:FC = () => {

     // 定义一个ref

    const inputRef = useRef<HTMLInputElement>(null) //dom节点

    const nameRef=useRef("pink") //不是dom节点,是一个普通的js变量

    function selectInput(){

        const input = inputRef.current;

        if(input){

            input.select(); // 选中输入框中的内容(Dom节点操作API)

        }

    }

  

    function changeName(){

        nameRef.current="blue"//修改ref的值,不引起rerender(state修改会触发组件更新)

        console.log(nameRef.current);

    }

    return (

        <>

        <input ref={inputRef} defaultValue={"hello world"}/>

        <button onClick={selectInput}>选中 input</button>

  

        <p>{nameRef.current}</p>

        <button onClick={changeName}>修改名字</button>

  

        </>

    )

}

  
  

export default Demo;

在这里插入图片描述

useMemo

缓存数据
不用每次执行函数组件(比如说 state修改)都重新生成 , 实现性能优化

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

  

const Demo : FC =()=>{

    const[num1,setNum1]=useState(0)

    const[num2,setNum2]=useState(0)

    const[text,setText]=useState("hello") //更新导致组件rerender

  
  

    const sum =useMemo(()=>{

        console.log("计算两数之和");//缓存

        return num1+num2;

    },[num1,num2])

  

    return (

        <>

        <p>sum = {sum}</p>

        <button onClick={()=>{setNum1(num1+1)}}>num1 : {num1}</button>

        <button onClick={()=>{setNum2(num2+1)}}>num2 : {num2}</button>

        <button onClick={()=>{setText(text+"1")}}>text : {text}</button>

        </>

    )

}

  
  
  

export default Demo;

在这里插入图片描述

useCallback

缓存函数

import {FC,useCallback,useState} from "react"

  
  

const Demo : FC =()=>{

    const [text,setText] = useState("hello")

    const fn1=()=>{

        console.log("fn1 text",text);

    }

  

    const fn2=useCallback(()=>{

        console.log("fn2 text",text);

    },[text])

  

    return(

        <>

        <button onClick={fn1}>fn1</button>

        <button onClick={fn2}>fn2</button>

        <div>

            <input value={text} onChange={e=>setText(e.target.value)}></input>

        </div>

        </>

    )

}

  
  
  

export default Demo;

自定义hooks

抽离公共部分,复用代码

同步案例

监听鼠标位置

  1. 自定义hook
import { useState,useEffect } from "react";

  

//获取鼠标位置

function useMouse(){

    const [x,setX]=useState(0);

    const [y,setY]=useState(0);

    const mouseMoveHandler=(e:MouseEvent)=>{

        setX(e.clientX);

        setY(e.clientY);

    }

  
  

    useEffect(()=>{

    //监听鼠标事件

        window.addEventListener('mousemove',mouseMoveHandler);

    //组件销毁时,一定要解绑DOM事件!!(可能会导致内存泄漏问题)

    return ()=>{

        window.removeEventListener('mousemove',mouseMoveHandler);

    }

    },[])

  
  

    return {x,y}

  

}

  
  

export default useMouse;
  1. app.tsx 中引入
//导入自定义hook


import useMouse from "./hooks/useMouse.ts";

function App() {


  const {x, y} = useMouse();

  return(

    <>

    <p>

      App Page

    </p>

    <div>x: {x}</div>

    <div>y: {y}</div>
    </>

  )

  

}

  

export default App

异步案例

模拟加载效果

import { useState,useEffect } from "react";

//异步获取消息

function getInfo():Promise<string>{

    return new Promise(resolve=>{

        setTimeout(()=>{

          resolve(Date.now().toString())  

        },1500)

    })

}

  
  

//自定义钩子

const useGetInfo=()=>{

    const[loading,setLoading]=useState(true)

    const[info,setInfo]=useState("")

  

    useEffect(()=>{

        getInfo().then(info=>{

            setLoading(false)

            setInfo(info)

        })

    },[])

    return {loading,info}

}

  
  

export default useGetInfo;

第三方hooks

提高开发效率

ahooks

react-hooks

hooks使用规则

  1. useXxx命名
  2. 组件内 获 其他hook 中可以调用
  3. 保证每次调用顺序一致(不能处于 if/for 内部)

面试题(闭包陷阱)

无法获取最新值

import {FC,useState} from 'react'

  

const Demo:FC =()=>{

    const[count,setCount]=useState(0)

    function add(){

        setCount(count+1)

    }

  

    function alertFn(){

      setTimeout(()=>{

        alert(count);

      },3000)

    }

  

    return(

        <>

        <p>闭包陷阱</p>

  

        <div>

        <span>{count}</span>

        <button onClick={add}>add</button>

        <button onClick={alertFn}>alert</button>

  

        </div>

        </>

    )

}

  
  

export default Demo;

使用ref解决在这里插入图片描述

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

  

const Demo:FC =()=>{

    const[count,setCount]=useState(0)

    const countRef = useRef(0)

  

    function add(){

        setCount(count+1)

    }

  

    useEffect(()=>{

        countRef.current=count

    },[count])

  

    function alertFn(){

      setTimeout(()=>{

        // alert(count); //count 值类型

        alert(countRef.current); // ref 引用类型

      },3000)

    }

  

    return(

        <>

        <p>闭包陷阱</p>

  

        <div>

        <span>{count}</span>

        <button onClick={add}>add</button>

        <button onClick={alertFn}>alert</button>

  

        </div>

        </>

    )

}

  
  

export default Demo;

在这里插入图片描述