2022-06-29 第四天:Hooks

一、useState Hook

1. 什么是useState Hook

useState 是一个 React Hook,它允许你向组件添加一个 状态变量

2. 使用

(1)引入并调用 useState(), 返回值包含 state数据修改state数据的方法,参数是state数据的初始值

const  [myState, setMyState] = useState()

(2)调用 修改state数据的方法 对state进行修改,注意 函数组件内没有this。

写法一:直接将最新值作为参数传入 setState(最新值)
写法二:函数作为参数,接收旧值,手动返回新值 setState((oldValue)=>newValue)

 1:  import React, { useState } from 'react';
 2:
 3:  function Example() {
 4:    const [count, setCount] = useState(100);
 5:
 6:    return (
 7:      <div>
 8:        <p>You clicked {count} times</p>
 9:        <button onClick={() => setCount(count + 1)}>
10:         Click me
11:        </button>
12:      </div>
13:    );
14:  }

二、useEffect Hook

1. 什么是useEffect Hook

useEffect 是一个 React Hook,它允许你 将组件与外部系统同步。(生命周期函数的另一种表现形式)

2. 使用

(1)引入并调用 useEffect,第一个参数是一个 回调函数,第二个参数是一个 数组

useEffect(()=>{ },[])

(2)第一个参数相当于 生命周期钩子; 第二个参数相当于 监听某些state数据的变化
(3)如果写了第二个参数,且数组中有值,则第一个函数会在 页面首次渲染、被监听的数据更新 时被调用。
(4)如果写了第二个参数,但是一个空数组,则第一个函数会在 页面首次渲染 时被调用。
(5)如果不写第二个参数,则第一个函数相当于componentDidMountcomponentDidUpdate,在 页面首次渲染 后和 任意state数据更新 后都会执行。

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

function Example() {
  const [count, setCount] = useState(0);

  // 无需清除的副作用:DOM首次渲染完成 或 更新后,设置标题
  useEffect(() => {
    document.title = `你点击了 ${count} 次`;
  });

  //  无需清除的副作用:只在DOM首次渲染完成时执行
  useEffect(()=>{
   console.log('DOM首次渲染完成!')
  },[])

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>点我加1</button>
    </div>
  );
}

(6)如果想要 清除副作用,在useEffect()的执行器函数中,返回一个函数,被返回的函数就相当于 componentWillUnmount,可以进行清除定时器等清除操作。
(将 useEffect() 放在 组件内部,让我们可以在 effect 中直接访问 state 变量 或其他 props

  handler=()=>{
   console.log('浏览器大小发生了变化')
  }
  useEffect(() => {
    window.addEventListener('resize',handler)
    return () => {
      window.removeEventListener('resize',handler)
    };
  });

三、useRef Hook

1. 什么是useRef Hook

useRef 是一个 React Hook,它能帮助我们获取组件信息,或者存储变量。

2. 用法一:获取子组件信息

(1)引入并调用 useRef

const myRef = useRef()

(2)给组件绑定ref(不能给函数组件直接绑定Ref)

<input ref={myRef} />

(3) 如果要给 函数组件 绑定ref,就要用到 \color{red}{ forwardRef}\color{red}{useImperativeHandle}

① 父组件内,定义Ref变量,并给子组件 绑定Ref。
② 子组件内,用 forwardRef()包裹子组件,同时从参数接收 父组件传递过来的ref。ref有以下两种用法:
③ 如果将ref绑定到了子组件的 某个元素上,则父组件可以获取到这个元素的 DOM对象
④ 如果使用 useImperativeHandle() 函数,则可以将 数据、方法 暴露给父组件。(第一个参数是 父组件传递过来的ref,第二个参数是一个回调函数。)

import {useRef} from 'react';

const Father= () => {
  const SonRef= useRef(null)
  return <Son ref={SonRef}></Son>
}
import { forwardRef, useImperativeHandle } from 'react'

const Son = forwardRef((props, ref) => {           // 1. 子组件接收到父组件传递的ref
    useImperativeHandle(ref, () => { return {} })  // 2.1. 可以将子组件的数据和方法暴露出去

     // 2.2 或者在子组件的HTML模版上绑定ref
    return <div ref={ref}>
        <button>子组件{count}</button>
    </div>
})

export default Son
3. 用法二:存储变量

使用 ref 存储变量可以确保:
(1)可以在函数组件重新渲染时 存储信息(state数据变化会驱动试图更新,同时函数组件会重新执行,普通对象存储的值会被重置)。
(2)改变它 不会触发重新渲染(state数据变化会触发重新渲染)。
(3)对于组件的每个副本而言,这些信息都是本地的(外部变量则是共享的)。

四、自定义Hook

1. 什么是自定义Hook

自定义Hook就是一个以use开头的自定义函数。通过自定义Hook,可以实现 逻辑的封装和复用

2. 使用

(1)定义一个 use开头的函数
(2)在函数体内封装 可复用 的逻辑代码。
(3)将函数内用到的 state数据 和 方法 return出去
(4)使用自定义Hook时,直接 调用函数,拿到state数据和方法。

五、 Hook基本使用规则

(1)只能在 React 的 函数组件 中调用 Hook。不能在普通 js函数或者类组件中调用。(自定义Hook中也可以调用Hook)
(2)只能在 函数组件最外层 调用 Hook。不要在 循环、条件判断 或者 子函数 中调用。
(3)如果想要有条件地执行一个 effect,可以将判断放到 Hook 的内部:

  useEffect(function persistForm() {
    // 将条件判断放置在 effect 中
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

(4)单个组件内,无论是State Hook或者Effect Hook,都可以 被多次调用。

六、useReducer Hook

1. 什么是useReducer

(1)userReducer是一个 React Hook,和useState相似都是用来 管理组件中的数据状态
(2)不同的是,userReducer修改数据时,可以根据 不同的参数 去触发 不同的代码逻辑

  • 跟Redux有点像,都包含三个核心概念:state数据、action对象、reducers存放同步方法。
  • 修改数据时调用dispatch函数,根据传入的action对象来触发reducers方法。
    (只是这里的action对象是自定义对象,而Redux中是调用函数生成的action对象。)
2. 使用

(1.1)导入 useReducer 钩子函数并调用。接收两个参数,返回值是一个数组。
(1.2)第一个数组元素是 state 数据,第二个数组元素是 dispatch 函数。
(1.3)第一个入参是 自定义的reducer函数;第二个入参是 state初始值

const [state, dispatch] = useReducer(myReducer, 初始值)

(2.1)自定义reducer函数,函数内部就是具体的业务逻辑。
(2.2)第一个参数是 state数据;第二个参数是 action对象,可以获取自定义参数,也可以判断要执行哪个业务逻辑。

function myReducer(state, action) {}

(3.1)调用 dispatch 函数,入参是一个 action 对象。
(3.2)action.type 用来区分要触发哪个业务逻辑;action.payload自定义参数

dispatch({type: '', payload: 自定义参数})

import { useReducer } from 'react'
// 自定义reducer函数
function myReducer(state, action) {
    switch (action.type) {
        case 'INC':
            return state + action.payload
        case 'DEC':
            return state - action.payload
        default:
            return state
    }
}

const Year = () => {
    const [state, dispatch] = useReducer(myReducer, 0) // 调用useReducer钩子函数
    return <div>
        <button onClick={()=>dispatch({type: 'INC', payload: 10})}>自增运算</button>
        <button onClick={()=>dispatch({type: 'DEC', payload: 20})}>自减运算</button>
    </div>
}

export default Year

七、useMemo Hook

1. 什么是useMemo

(1)useMemo 是一个 React Hook,它在组件每次 重新渲染 的时候能够 缓存计算的结果,只有 指定的依赖项 变化时,才会重新 执行计算过程
(2)数据驱动视图:React组件中任意state数据变化,都会引发组件 重新渲染,组件中调用的方法都会 重新执行
(3)应用场景:消耗非常大的计算逻辑

2. 使用

(1)导入 useMemo 钩子函数并调用。接收 两个参数
(2)第一个参数是一个 回调函数,函数内部用来书写具体代码逻辑,函数必须有 返回值
(3)第二个参数是 依赖项数组,可以是state数据、props数据、自定义变量和函数等。

const result = useMemo(()=>{ return num+1 }, [num])

import { useMemo, useState } from 'react'

// 计算量比较大的逻辑代码
function fn (value) { return value+ 1}

const Test = () => {
    // 1.定义state数据,作为useMemo的依赖项
    const [num ,setNum] = useState(0) 
    // 2.使用useMemo包裹计算量较大的代码逻辑,必须有返回值
    const result = useMemo(()=>{
        return fn(num)
    }, [num])

    return <div> </div>
}

export default Test

八、useCallback Hook

1. 什么是useCallback

(1)useCallback是一个React Hook, 主要用于性能优化,它的核心作用是 缓存函数引用,避免不必要的重渲染。

2. 使用
使用场景 说明 示例
子组件依赖函数props 当函数作为props传递给被 memo 优化的子组件时 子组件用 React.memo 包裹,父组件的函数需要用 useCallback 缓存
依赖数组的依赖项 当函数作为其他 Hook 的依赖项时 useEffect、useMemo 等的依赖数组中包含函数
稳定的函数引用 需要保持函数引用稳定的特殊场景 第三方库依赖、事件监听器等需要稳定函数引用

九、memo AIP

1. 什么是memo

(1)memo是一个函数,可以让子组件在 props 没有变化时,跳过重新渲染的过程。
(2)React渲染机制:父组件发生重新渲染时,子组件 也会 重新渲染

2. 使用

(1)导入memo函数,并用memo函数 包裹子组件,返回一个新的 mome子组件

const memoComponent = memo(function myComponent(props){ } )

3. 判断props是否变化的机制

(1)React使用 Object.is() 方法判断新旧props是否发生变化。对于引用数据类型它比较的是数据的引用地址。
(2)对于 引用数据类型,如 数组、对象、函数,组件每次渲染时都会产生新的数据,props 必然发生变化
(3)如果想对 \color{green}{数组、对象} 稳定引用,可以在父组件中用 \color{green}{useMemo} 钩子函数包裹数组或对象,暂时缓存。

const arr = [1,2,3]
const newArr = useMemo(()=>{return arr},[ ])

(4)如果想对 \color{green}{函数} 稳定引用,可以在父组件中用 \color{green}{useCallback} 钩子函数包裹函数,暂时缓存。\color{red}{(父子传值时传递自定义函数)}

const handleClick = useCallback((value)=>{console.log(value)},[ ])

十、 第三方Hooks插件

1. ahooks 的 useGetState 是一个自定义 Hook,用于解决在异步操作中获取最新 state 值的问题。

(1)在 React 中,直接使用 useState 时,如果在异步回调(如 setTimeout、Promise、事件监听等)中访问 state,获取到的是闭包中的旧值,而不是最新的 state 值。

import { useGetState } from 'ahooks';

function SolutionComponent() {
  const [count, setCount, getCount] = useGetState(0);

  const handleAsync = () => {
    setTimeout(() => {
      console.log(getCount()); // 这里获取的是最新的 count 值
      // 无论何时调用,都能获取到最新的 state
    }, 1000);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={handleAsync}>异步打印最新值</button>
    </div>
  );
}
2. useAsyncEffect 是 ahooks 中的一个自定义 Hook,用于在 useEffect 中处理异步操作。

(1)在 React 的原生 useEffect 中,不能直接使用 async/await,因为 async 函数会返回 Promise,而 useEffect 应该返回清理函数或不返回任何内容。

import { useAsyncEffect } from 'ahooks';

function SolutionComponent() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useAsyncEffect(async () => {
    try {
      setLoading(true);
      const result = await fetch('/api/data');
      const jsonData = await result.json();
      setData(jsonData);
    } catch (error) {
      console.error('获取数据失败:', error);
    } finally {
      setLoading(false);
    }
  }, []);

  if (loading) return <div>加载中...</div>;
  return <div>数据: {JSON.stringify(data)}</div>;
}

十一、React内置组件

1. Fragment

无论是类组件、函数组件中,HtML结构最外层的<div></div>标签,可以替换为:

<Fragment> </Fragment> 或是 <> </>

① Fragment可以接收一个 key属性 用于遍历时的 唯一标识。
② 空标签不能接收任何属性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容