Blog
首页
文档
收藏
关于
  • 在线转换时间戳 (opens new window)
  • 在线压缩图片 (opens new window)
  • Float-Double转二进制 (opens new window)
  • 文件转Hex字符串 (opens new window)

HiuZing

🍑
首页
文档
收藏
关于
  • 在线转换时间戳 (opens new window)
  • 在线压缩图片 (opens new window)
  • Float-Double转二进制 (opens new window)
  • 文件转Hex字符串 (opens new window)
  • 前端面试题

  • JavaScript

  • Vue2

  • port

  • CSS

  • Node.js

  • JavaScript优化

  • uniapp

  • Mini Program

  • TypeScript

  • 面向对象编程

  • UI组件

  • Plugin

  • Vue3

  • 性能优化

  • Axios

  • 状态管理

  • React

    • 教程

      • React 简介
      • React 组件
      • React 应用
      • React AJAX
      • React Router5
      • React Redux
      • React 扩展内容
      • React Router6
      • React Router Hooks
      • React Hooks
        • useState
          • 语法
          • 参数
          • 实例
          • 基础用法
          • 注意事项
        • useContext
          • 实例
        • useRef
          • 语法
          • 作用
          • 实例
          • 注意事项
        • forwardRef
        • useImperativeHandle
        • useEffect
          • 注意事项
        • useReducer
        • useMemo
        • useCallback
        • useImperativeHandle
        • useLayoutEffect
        • useTransition
          • 语法
          • 解决问题
          • 注意事项
        • useDeferredValue
          • 案例
    • 实战

  • Mock

  • Icon

  • Template

  • 构建工具

  • 项目规范配置

  • Taro

  • SVG

  • React Native

  • 前端
  • React
  • 教程
HiuZing
2023-05-11
目录

React Hooks

# useState

让函数组件也可以有state状态, 并进行状态数据的读写操作

# 语法

const [xxx, setXxx] = React.useState(initValue)
1

# 参数

-initValue:第一次初始化指定的值在内部作缓存

-xxx:内部当前状态值

-setXxx:更新状态值的函数

# 实例

// setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
setCount(count + 1)

// setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
setCount(count => count + 1)
1
2
3
4
5

# 基础用法

function State(){
  const [count, setCount] = useState(0);
  return (
      <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
              Click me
          </button>
      </div>
  )
}
1
2
3
4
5
6
7
8
9
10
11

# 注意事项

  1. 每当状态变化时,会触发函数组件重新执行,从而根据最新的数据更新渲染DOM结构

    export function Count(){
        const [count,setCount] = useState(0);
        
        console.log("每次count值发生变化,都会打印");
        
        const add = ()=>{
            setCount(count +1)
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 除了可以给定初始值,还可以通过函数返回值的形式,为状态赋初始值

    export const DateCom = ()=>{
        const [date,setDate] = useState(()=>{
            const dt = new Date()
            return {
                year:dt.getFullYear(),
                month:dt.getMonth()+1,
                day:dt.getDate()
            }
        })
        
        return {
            <>
            	<Text>年份:{date.year}</Text>
            </>
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  3. usetate是异步变更状态,修改后无法立即拿到最新的状态

  4. 结合useEffect监听状态的变化,执行相对应的回调函数

    useEffect(()=>{依赖变化时,触发的函数},[依赖项])
    
    1
  5. 更新不及时的bug,连续多次以相同更新状态值,如果值相同会屏蔽后续的更新行为,从而防止组件频繁渲染的问题

    强调:当我们修改state状态的时候,如果我们发现:新值依赖于旧值(基于旧值进行计算,最终得到新值)

    此时,不要直接在外部进行计算,而是要通过fn函数的形参拿到旧值,并进行计算,最终return新值

    const [count,setCount] = useState(()=>0)
    
    const add = ()=>{
        setCount(count + 1)
        setCount(count + 1)
    }
    
    
    // 改成
    // 基于prev计算并return一个新值
    const add = ()=>{
        setCount((prev)=>prev + 1)
        setCount((prev)=>prev + 1)
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  6. 更新对象类型的值

    set函数内部,会对更新前后的值进行对比

    const [user,setUser] = useState({
        name:'hhh',
        age:2
    })
    // 用新对象的引用类型替换旧对象的引用,即可正常触发组件的重新渲染
    const changeUserInfo = ()=>{
        user.name = 'aaa'
     	// 1.
        setUser({...user})
        // 2.
        setUser(Object.assign({},user))
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  7. 使用setState模拟组件的强制刷新

    因为每次传入的对象地址不一样,所以会组件刷新

    export const FnUpdate = ()=>{
        const [,forceUpdate] = useState({})
        
        // 每次调用onRefresh函数,都会给forceUpdate传递一个新对象,从而触发组件重新渲染
        const onRefresh = ()=>forceUpdate({})
    }
    
    1
    2
    3
    4
    5
    6

# useContext

用来处理多层级传递数据的方式

# 实例

  1. 使用 React Context API,在组件外部建立一个 Context

    import React from 'react';
    const ThemeContext = React.createContext(0);
    export default ThemeContext;
    
    1
    2
    3
  2. 使用 Context.Provider提供了一个 Context 对象,这个对象可以被子组件共享

    import React, { useState } from 'react';
    import ThemeContext from './ThemeContext';
    import ContextComponent1 from './ContextComponent1';
    
    function ContextPage () {
      const [count, setCount] = useState(1);
      return (
        <div className="App">
          <ThemeContext.Provider value={count}>
            <ContextComponent1 />
          </ThemeContext.Provider>
          <button onClick={() => setCount(count + 1)}>
                  Click me
          </button>
        </div>
      );
    }
    
    export default ContextPage;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  3. useContext()钩子函数用来引入 Context 对象,并且获取到它的值

    // 子组件,在子组件中使用孙组件
    import React from 'react';
    import ContextComponent2 from './ContextComponent2';
    function ContextComponent () {
      return (
        <ContextComponent2 />
      );
    }
    export default ContextComponent;
    
    
    // 孙组件,在孙组件中使用 Context 对象值
    import React, { useContext } from 'react';
    import ThemeContext from './ThemeContext';
    function ContextComponent () {
      const value = useContext(ThemeContext);
      return (
        <div>useContext:{value}</div>
      );
    }
    export default ContextComponent;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

# useRef

可以在函数组件中存储/查找组件内的标签或任意其它数据

# 语法

const refContainer = useRef()
1

# 作用

  1. 保存标签对象,功能与React.createRef()一样

  2. 获取DOM元素和子组件实例对象

  3. 存储渲染周期之间共享的数据

    用于存储上一次旧count值,每当点击按钮触发count自增时,都把最新的旧值赋值

# 实例

  1. 获取DOM元素

    function show() {
    	alert(myRef.current.value)
        // 让文本框获取焦点
        myRef.current?.focus
    }
    
    <input type="text" ref={myRef}/>
    
    1
    2
    3
    4
    5
    6
    7
  2. 存储渲染周期之间共享的数据

    useRef只有在组件首次渲染的时候被创建

    如果组件是重新渲染的时候,不会重复创建ref对象,因此可以保存旧值

    const [count,setCount] = useState(0)
    const prevCountRef = useRef<number>()
    
    const add = ()=>{
        setCount((prev)=>prev + 1)
        prevCountRef.current = count
    }
    
    1
    2
    3
    4
    5
    6
    7

# 注意事项

  1. 组件rerender时useRef不会被重复初始化

    const [count,setCount] = useState(0)
    // rerender后刷新后不变化
    const time = useRef(Date.now())
    
    1
    2
    3
  2. ref.current变化时不会造成组件rerender

    const [count,setCount] = useState(0)
    // rerender后刷新后不变化
    const time = useRef(Date.now())
    
    const updateTime = ()=>{
        time.current = Date.now()
        // 更新时间log执行,但是页面没有更新
        console.log(time.current)
    }
    // 更换ref值后,没有执行
    console.log('组件渲染了')
    
    return(
    	<>
        	<h3>{count},{time.current}</h3>
        </>
    )
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  3. ref.current不能作为其他Hooks的依赖项

    useEffect会在组件首次渲染完毕之后,默认执行一次,组件每次渲染完毕之后,会触发useEffect中的回调函数,如果给了依赖项数组,则还要判断依赖项是否变化,再决定是否触发回调

# forwardRef

React一个API

在使用函数组件,无法直接使用ref引用函数式组件

<Child ref={inputRef} />
1

因为默认情况下,自己组件不会暴露内部的DOM节点的ref

解决方法

使用React.forwardRef()函数式组件包装起来

// props用的时候,将_改成props
const Child = React.forwardRef((_,ref)=>{
    // 导出的对象父组件才能拿到
    useImperativeHandle(ref,()=>({
        
    }))
    
})
1
2
3
4
5
6
7
8

# useImperativeHandle

  1. 以上解决方法是基本使用

  2. 基于useImperativeHandle按需向外暴露成员

    const [count,setCount] = useState(0)
    
    const Father = ()=>{
        const onReset = ()=>{
            childRef.current?.setCount(0)
        }
        
        const childRef = useRef<>()
        
        <Child ref={childRef} />
    }
    
    
    const Child = React.forwardRef((_,ref)=>{
        useImperativeHandle(ref,()=>({
            count,
            setCount
        }))
        
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  3. 控制成员暴露的粒度

    const Child = React.forwardRef((_,ref)=>{
        // 向外暴露count的值和reset函数
        useImperativeHandle(ref,()=>({
            count,
            // 在组件内部封装一个重置为0的函数,API的粒度更小
            reset:()=>setCount(0)
        }))
        
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  4. useImperativeHandle第三个参数

    1. 空数组:子组件首次渲染时,执行useImperativeHandle中的反调,从而把return的对象作为父组件接收到的ref

      useImperativeHandle(ref,()=>({
          count,
          // 在组件内部封装一个重置为0的函数,API的粒度更小
          reset:()=>setCount(0),
          []
      }))
      
      1
      2
      3
      4
      5
      6
    2. 依赖项数组

# useEffect

除了返回值外对外界环境造成其他影响,即与组件渲染无关的操作。

可以把 useEffect Hook 看做如下三个函数的组合(componentDidMount()、componentDidUpdate()、componentWillUnmount())

React中的副作用操作:

  1. 发ajax请求数据获取
  2. 设置订阅 / 启动定时器
  3. 手动更改真实DOM

# 语法

 useEffect(() => { 
     // 在此可以执行任何带副作用操作
     return () => { // 在组件卸载前执行
         // 在此做一些收尾工作, 比如清除定时器/取消订阅等
     }
 }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行,数组有值就(componentDidUpdate、componentWillUnmount)
1
2
3
4
5
6

# 实例

  1. 执行时机

    没有为指定依赖项数组,会在函数组件每次渲染完成后执行

    useEffect(()=>{
        console.log('好好好好')
    })
    
    1
    2
    3
  2. 空数组

    只会在组件首次渲染完成执行后唯一一次

    React.useEffect(() => {
        let timer = setInterval(() => {
            setCount(count => count + 1)
        },1000)
        return ()=>{
            console.log(1)
            clearInterval(timer)
        }
    },[])
    
    function unmount() {
    	root.unmount()
    }
    
    <button onClick={unmount}>点我卸载组件</button>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  3. 依赖项

    每次渲染完毕后,判断依赖项是否变化,再决定是否执行副作用函数

  4. 清理副作用

    清理网络请求

    清理事件绑定

    useEffect(()=>{
        //1.执行副作用操作
        //2.返回一个清理副作用的函数
        return()=>{
            // 在这里执行自己的清理操作
        }
    },[依赖项])
    
    1
    2
    3
    4
    5
    6
    7

# 注意事项

  1. 会在组件首次渲染完毕之后,默认执行一次
  2. 不要在useEffect中改变依赖项的值,会造成死循环
  3. 多个不同功能的副作用尽量分开声明,不要写到一个useEffect中

# useReducer

比 useState 更适用的场景:例如 state 逻辑处理较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等

# useMemo

缓存值

当父组件重新渲染时,子组件也会重新渲染,即使子组件的 props 和 state 都没有改变

// 父组件
import {useState} from 'react'
function IndexPage(){
    const [value,setValue] = useState('')
    return(
    	<>
        	<div>
        	<input value={value} onChange={(ev)=>setValue(ev.target.value)}/>	
        </div>
        </>
    )
}
// 子组件
import {memo} from 'react'

const Child = ({count}) => {
    const show = () => {
        console.log('子组件渲染')
    }
    return (
	    <>
        	<h3>Child组件</h3>
        	<div>{count}</div>
        </>
    )
}
export default memo(Child)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# useCallback

缓存函数

假设需要将事件传给子组件,当点击父组件按钮时,发现控制台会打印出子组件被渲染的信息,说明子组件又被重新渲染了

// 父组件
import {useState,useCallback} from 'react'
function IndexPage(){
    const [value,setValue] = useState('')
	// [] 执行一次
    // [count] 每次改变count都执行一次
    const updateCount = useCallback(()=>console.log('父业务'),[])
    return(
    	<>
        	<div>
        	<input value={value} onChange={(ev)=>setValue(ev.target.value)}/>
            <Child updateCount = {updateCount} />
        </div>
        </>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# useImperativeHandle

# useLayoutEffect

# useTransition

# 语法

import {useTransiton} from 'react'

function TabContainer(){
    const [isPending,startTransiton] = useTransiton()
}
1
2
3
4
5

# 参数

调用useTransiton时不需要传递任何参数

# 返回值

  1. isPending:是否存在待处理的transition,如果为true,说明页面上存在待渲染的部分
  2. startTransiton:调用此函数,可以把状态的更新标记为低优先级,不阻塞UI对用户的操作响应

# 解决问题

export const Tabs = ()=>{
    const [active,setActive] = useStste('home')
    // 新增
	const [isPending,startTransition] = useTransition()
    
    const click = (name)=>{
        // 把某次更新标记,标记为低优先级,从而防止页面卡顿情况
        startTransition(()=>{
            setActive(name)
        })
		
    }
    
    return (
    	<>
        	<View onClick={()=>click('home')}>home</View>
        	<View onClick={()=>click('movie')}>movie</View>
        	<View onClick={()=>click('about')}>about</View>
        </>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

使用isPending展示加载状态

  1. 使用useTransition期间,接收isPending参数

    const [isPending,startTransition] = useTransition()
    
    1
  2. 将标签页的渲染,抽离到renderTabs函数中

    const renderTabs = ()=>{
        if(isPending) return <h3>Loading...</h3>
        switch(activeTab){
    		case'home':
                return <Home/>
            case'movice':
                return <Movie/>
            case'about':
                return <About/>
            default:
                return <Home/>
        }
    } 
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  3. 调用renderTabs函数,渲染标签页到组件中

    {renderTabs()}
    
    1

# 完整案例

export const Tabs = ()=>{
    const [active,setActive] = useStste('home')
    // 新增
	const [isPending,startTransition] = useTransition()
    
    const click = (name)=>{
        // 把某次更新标记,标记为低优先级,从而防止页面卡顿情况
        startTransition(()=>{
            setActive(name)
        })
    }
    
    const renderTabs = ()=>{
        if(isPending) return <h3>Loading...</h3>
        switch(activeTab){
            case'home':
                return <Home/>
            case'movice':
                return <Movie/>
            case'about':
                return <About/>
            default:
                return <Home/>
        }
} 
    
    return (
    	<>
        	<View onClick={()=>click('home')}>home</View>
        	<View onClick={()=>click('movie')}>movie</View>
        	<View onClick={()=>click('about')}>about</View>
        
        {renderTabs()}
        </>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 注意事项

  1. 传递给startTransition的函数必须是同步的,React会立即执行此函数,并将在其执行期间发生的所有状态更新标记为transition。如果在其执行期间,尝试稍后执行状态更新(例如在一个定时器中执行状态更新)。这些状态更新不会被标记为trainsition
  2. 标记为transition的状态更新将被其他状态更新打断。例如,在transition中更新图表组件,并在图表组件仍在重新渲染时继续输入框输入,react将首先处理输入框的更新。之后再重新启动对图表组件的渲染工作
  3. transition更新不能用于控制文本输入

# useDeferredValue

提供一个state的延迟版本,根据返回的延迟的state能够推迟更新UI中某一部分,从而达到性能优化

function Search(){
    const [kw,setKw] = useState('')
    const deferred = useDeferredValue(kw)
}
1
2
3
4

useDeferredValue返回值是一个延迟班的状态

  1. 在组件首次渲染期间,返回值与传入的值相同

  2. 在组件更新期间,react将首先使用旧值重新渲染UI结构,能够跳过某些复杂组件的rerender,从而提高渲染效率

    随后,react将使用新值更新useDeferredValue, 并在后台使用新值重新渲染的一个低优先级的更新。意味着后台使用新值更新时value再次改变,它将打断那次更新

# 案例

function Search(){
    const [kw,setKw] = useState('')
    const deferred = useDeferredValue(kw)
    
    const onInput = (e)=>{
        setKw(e.currentTarget.value)
    }
    
    return {
        <>
        	<input onChange={onInput} value={} />
       
        	<SearchResult query={deferred}/>
        </>
    }
}

// 子组件 当props没有变化时会跳过子组件的rerender
const SearchResult:React.FC<{query:string}>=React.memo((props)=>{
    if(!props.query) return
    const items = Array(4000).fill(props.query)
   .map((item,i)=><p key={i}>{item}<p/>)
            
	return items
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#React
上次更新: 2024/08/14, 04:14:33
React Router Hooks
React权限控制

← React Router Hooks React权限控制→

最近更新
01
React Native 使用SVG
08-13
02
Docker基础命令
08-04
03
算数逻辑单元
07-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 WeiXiaojing | 友情链接
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式