React 基础hook的使用

214 阅读4分钟

简单理解

  • 由于class组件难以理解,代码非常冗余。hook使你在非class的情况下可以使用更多的React特性,拥抱函数。

useState

  • 用来定义 state变量和更新state的方法

代码示例

import { useState } from 'react';
export default function Demo() {
  // 声明一个叫 myNum 的 state 变量,setMyNum为更新myNum的方法
  let [myNum, setMyNum] = useState(0)
  // 更新myNum数据
  function upMyNum() {
    setMyNum((val) => {
      // val 为更新前的数据
      let num = val + 1
      return num
    })
  }
  // 声明一个叫 myObj 的 state 变量,setMyObj为更新myObj的方法
  let [myObj, setMyObj] = useState({
    name: 'hahahaah',
    age: 18
  })
  // 更新myObj数据
  function upMyObj() {
    setMyObj(({ name, age }) => {
      // name age为更新前的数据
      console.log(name, age)
      return {
        name: 'xixiixix',
        age: 30
      }
    })
  }
  return (
    <div>
      <div>{myNum}</div>
      <button onClick={upMyNum}>加1</button>
      <div>名字{myObj.name};年龄{myObj.age}</div>
      <button onClick={upMyObj}>更改对象数据</button>
    </div>
  )
}
  • 调用useState中更新数据方式时,是直接做替换
  • 在一个函数中useState,可定义多组数据

useEffect

  • 类似于class中 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

代码示例

export default function Demo() {
  let [myNum, setMyNum] = useState(0)
  function upMyNum() {
    setMyNum((val) => {
      let num = val + 1
      return num
    })
  }
  // dom第一次渲染和每次更新之后都会执行useEffect
  useEffect(()=>{
    console.log('useEffect1')
  })
  // dom第一次渲染后执行一次
  useEffect(() => {
    console.log('useEffect2')
  },[])
  // dom第一次渲染后执行一次,后续只有myNum更新后执行
  useEffect(() => {
    console.log('useEffect3')
  }, [myNum])
  // 当useEffect返回一个函数,组件卸载时将会在执行清除操作时调用它
  useEffect(() => {
    console.log('useEffect4')
    return ()=>{
      console.log('清除useEffect4')
    }
  })
  return (
    <div>
      <div>{myNum}</div>
      <button onClick={upMyNum}>加1</button>
    </div>
  )
}
  • 默认情况下,它在第一次渲染之后和每次更新之后都会执行useEffect。
  • 在一个函数中useEffect,可使用多次

useContext Provider Consumer

  • Context的作用就是对它所包含的组件树提供全局共享数据的一种技术。

代码示例

import { useState, createContext, useContext, } from 'react';
interface Myfrom {
  n: number,
  setN: Function
}
// 定义MyContext
const MyContext = createContext({} as Myfrom);
export default function Demo() {
  const [n, setN] = useState(0)
  return (
    <div>
      <MyContext.Provider value={{ n, setN }}>
        <div>这是一级</div>
        <button onClick={() => setN(n + 1)}>一级加</button>
        <Demo2></Demo2>
      </MyContext.Provider>
    </div>
  )
}
// 使用Consumer接收
function Demo2() {
  return (
    <MyContext.Consumer >
      {
        ({ n, setN }) => {
          return (
            <div>
              <div>这是二级:n:{n}</div>
              <button onClick={() => setN(n + 1)}>二级加</button>
              <Demo3></Demo3>
            </div>
          )
        }
      }
    </MyContext.Consumer>
  )
}
// 使用useContext接收
function Demo3() {
  // 使用上下文,因为传入的是对象,则接受也应该是对象
  const { n, setN } = useContext(MyContext)
  return (
    <div>
      这是三级:n:{n}
      <button onClick={() => setN(n + 1)}>三级加</button>
    </div>
  )
}
  • 一般Provider和Consumer,都是成对出现
  • useContext的作用与Consumer相同,只是写法上的区别
  • useContext和redux的作用是不同的,虽然功能上有类似。前者解决组件嵌套传值,后者解决应用中统一管理状态

useMemo

  • 缓存变量
  • 所依赖的值改变时,才会重新调用回调函数,并返回计算后的值。
  • 避免在每次渲染时都进行高开销的计算

代码示例

const App = function () {
  let [num, setNum] = useState(0)
  let newNum = useMemo(() => {
    return num * 2
  }, [num == 3])
  return (
    <div>
      <div>num:{num}</div>
      <div>newNum:{newNum}</div>
      <button onClick={() => setNum(num + 1)}>加</button>
    </div>
  )
}
  • 以上代码,newNum会重新计算两次。第一次num === 3由flase变为ture,第二次num === 3由ture变为false
  • 如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
  • 不要在useMemo内部执行与渲染无关的操作

memo

  • 与React.PureComponent类似,memo用于函数式组件
  • 接受2个参数,第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与shouldComponentUpdate()功能类似。

代码示例

import React from "react";
function Child({seconds}){
    console.log('I am rendering');
    return (
        <div>I am update every {seconds} seconds</div>
    )
}
// 比较函数,控制是否更新
function areEqual(prevProps, nextProps) {
    if(prevProps.seconds===nextProps.seconds){
        return true
    }else {
        return false
    }
}
export default React.memo(Child,areEqual)
  • 默认情况memo是通过浅比较,来确认子组件是否渲染

useCallback

  • 缓存函数
  • 与useMemo基本类似,只是useMemo返回的是计算结果。useCallback返回的是函数
  • useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

使用memo;useCallback;useMemo优化子组件渲染

  • 正常情况每一次setData,都会重新渲染当前组件和子组件
  • 默认情况memo是通过浅比较,来确认子组件是否渲染
  • 当父组件传入子组件的参数过于复杂,或者传入方法时(方法都为引用类型)memo无法识别

代码示例

import { useState, useMemo, useCallback, memo } from 'react';
const Child = memo(function (prop) {
  console.log('Child渲染')
  let { newNum, childEvent } = prop
  return (
    <div>
      <div>newNum:{newNum}</div>
      <div onClick={childEvent}>点击</div>
    </div>
  )
})
const App = function () {
  console.log('App渲染')
  let [num, setNum] = useState(0)
  let [txt, setTxt] = useState('')
  let newNum = useMemo(() => {
    return num * 2
  }, [num == 3])
  let childEvent = useCallback(() => {
    console.log('子组件触发了我')
    setTxt('子组件触发了我')
  }, [num == 6])
  return (
    <div>
      <div>num:{num}</div>
      <div>newNum:{newNum}</div>
      <div>txt:{txt}</div>
      <button onClick={() => setNum(num + 1)}>加</button>
      <div>-------------以下是子组件---------------</div>
      <Child newNum={newNum} childEvent={childEvent}></Child>
    </div>
  )
}
export default App
  • 只要调用setData(无论父调用还是子调用),App组件都会重新渲染
  • 上面代码中,Child只会渲染5次,第一次初次加载,在num == 3和num == 6的值变化时,分别有两次。

待续。。。。。。。