react hooks

hooks简介

useActionState – React 中文文档

官网文档

介绍Hooks之前,首先要说一下React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类。

但是,在以往开发中类组件和纯函数组件的区别是很大的,纯函数组件有着类组件不具备的多种特点:

  • 纯函数组件没有状态
  • 纯函数组件没有生命周期
  • 纯函数组件没有this

这就注定,我们所推崇的函数组件,只能做UI展示的功能,涉及到状态的管理与切换,不得不用类组件或者redux,但类组件也是有缺点的,比如,遇到简单的页面,代码会显得很重,并且每创建一个类组件,都要去继承一个React实例

React Hooks: 就是用函数的形式代替原来的继承类的形式,并且使用预函数的形式管理state,有Hooks可以不再使用类的形式定义组件了。

这时候认知也要发生变化了,原来把组件分为有状态组件和无状态组件,有状态组件用类的形式声明,无状态组件用函数的形式声明。那现在所有的组件都可以用函数来声明了。

使用Hooks的优点:

  • 告别难以理解的Class( this 和 生命周期 的痛点)
  • 解决业务逻辑难以拆分的问题
  • 使状态逻辑复用变得简单可行
  • 函数组件从设计思想上来看更加契合React的理念

Hooks并非万能:

  • Hooks暂时还不能完全的为函数组件补齐类组件地能力(如生命周期的getSnapshotBeforeUpdate、componentDidCatch方法暂时还未实现)
  • 将类组件的复杂变成函数组件的轻量,可能使用者并不能很好地消化这种复杂
  • Hooks在使用层面有着严格地规则约束
import React, {Component} from "react";
class AddCount extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count : 0
        }
    }
    addcount = () => {
        let newCount = this.state.count;
        this.setState({
            count: newCount += 1
        })
    }
    render() {
        return (
            <>
                <p>{ this.state.count }</p>
                <button onClick={ this.addcount }>count++</button>
            </>
        )
    }
}
export default AddCount;

可以看出来,上面的代码确实很重。因此React队设计了React Hooks。React Hooks就是加强版的函数组件,可以完全不使用 class,就能写出一个全功能的组件

React Hooks 使得组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。而React Hooks 就是所说的“钩子”。

那么Hooks要怎么用呢?“你需要写什么功能,就用什么钩子”。对于常见的功能,React为我们提供了一些常用的钩子,当然有特殊需要,我们也可以写自己的钩子。下面是React Hooks为我们提供的默认的四种最常用钩子:

  • useState()
  • useContext()
  • useEffect()
  • useReducer()

不同的钩子为函数引入不同的外部功能,上面四种钩子都带有use前缀,React Hooks约定,钩子一律使用use前缀命名。所以,自己定义的钩子都要命名为useXXX。

Hook函数

1. useState

useState():状态钩子。纯函数组件没有状态,用于为函数组件引入state状态, 并进行状态数据的读写操作
语法、参数及返回值说明::

const [xxx, setXxx] = React.useState(initValue) 
  • 参数: 第一次初始化指定的值在内部作缓存
  • 返回值: 包含2个元素的数组,第1个为内部当前状态值,第2个为更新状态值的函数

setXxx()2种写法:

  • setXxx(newValue): 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
  • setXxx(value => newValue): 参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值
import React,{ useState } from "react";

const NewCount = ()=> {
    const [ count,setCount ] = useState(0)
    addCount = ()=> {
        let newCount = count;
        setCount(newCount +=1)
    }
   return (
       <>
           <p> { count }</p>
           <button onClick={ addCount }>Count++</button>
       </>
   )
}
export default NewCount;

代码看起来更加的轻便简洁,没有了继承,没有了渲染逻辑,没有了生命周期

2. useContext

useContext():共享状态钩子。作用就是可以做状态的分发,在React16.X以后支持,避免了react逐层通过Props传递数据。

跨组件共享数据的钩子函数,接收一个context对象,并返回该对象的当前值。

当前的context值由上层组件中距离当前组件最近的<MyContext.Provider>的value决定,并且父组件的context发生改变是,子组件都会重新渲染

使用语法和说明:

创建Context容器对象:

const XxxContext = React.createContext()  

渲染子组件时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:

<xxxContext.Provider value={数据}>
	<子组件/>
</xxxContext.Provider>

后代组件读取数据:

const {} = useContext(XxxContext)
import React, { useContext } from "react";
const HookTest = ()=> {
    const AppContext = React.createContext();

	//子组件
    const A = ()=> {
        const { name } = useContext(AppContext)
        return (
            <p>
                我是A组件,我的名字是:{ name };
                <span>我是A的子标签:{ name }</span>
            </p>
        )
    }
	//子组件
    const B= ()=> {
        const { name } = useContext(AppContext);
        return (
            <p>我是B组件,名字是: { name }</p>
        )
    }
    return (
        <AppContext.Provider value={{ name: '张三'}}>
            <A />
            <B />
        </AppContext.Provider>
    )
}
export default HookTest;

3. useEffect

useEffect():副作用钩子。用来更好的执行副作用操作(用于模拟类组件中的生命周期钩子),如异步请求等,在类组件中会把请求放在componentDidMount里面,在函数组件中可以使用useEffect()

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

参数、返回值说明:

  • useEffect()接受两个参数,第一个参数是要进行的异步操作第二个参数是一个数组,用来给出Effect的依赖项,只要这个数组发生变化,useEffect()就会执行。
  • 当第二项省略不填时。useEffect()会在每次组件渲染时都会执行useEffect,只要更新就会执行。
  • 当第二项传 空数组[ ] 时,只会在组件挂载后运行一次。
  • useEffect()返回值可以是一个函数,在组件销毁的时候会被调用。清理这些副作用可以进行如取消订阅、清除定时器操作,类似于componentWillUnmount。

React中的副作用操作:

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

useEffect两个注意点:

React首次渲染和之后的每次渲染都会调用一遍useEffect函数,而之前要用两个生命周期函数分别表示 首次渲染(componentDidMonut) 和更新导致的重新渲染(componentDidUpdate)

useEffect中定义的函数的执行不会阻碍浏览器更新视图,也就是说这些函数时异步执行的,而componentDidMonut和componentDidUpdate中的代码都是同步执行的。

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

1、类似于componentDidMount的useEffect

import { useEffect } from 'react'

const Demo = () => {
  useEffcet(() => {
	console.log('类似于componentDidMount,通常在此处调用api获取数据')
  }, [])
}

export default Demo

2、类似于componentWillUnmount的useEffect

import { useEffect } from 'react'

const Demo = () => {
  useEffcet(() => {
	return () => {
	  console.log('类似于componentWillUnmount,通常用于清除副作用');
	}
  }, [])
}

export default Demo

3、类似于componentDidUpdate的useEffect

import { useState,useEffect } from 'react'

const Demo = () => {
  const [count,setCount] = React.useState(0)
	
  useEffcet(() => {
	console.log('当count发生改变时,执行当前区域的代码')
  }, [count])
}

4. useId

act Hooks 中的 useId 是一个自定义钩子函数,用于生成唯一的 ID。它可以帮助我们在 React 组件中生成一个全局唯一的标识符。

下面是一个使用 useId 的示例:

import React from 'react';
import { useId } from 'react-id-generator';

function ExampleComponent() {
  const [id] = useId();

  return <div id={id}>Example Component</div>;
}

在上面的示例中,我们通过调用 useId 函数来生成一个唯一的 id。然后,我们将这个 id 应用到组件的 div 元素上。

useId 函数返回一个包含唯一 id 的数组。我们使用数组解构来获取这个唯一 id 并将其应用到组件中。

这样做的好处是,我们可以在需要唯一标识符的地方直接使用 useId,而不必手动编写生成唯一 ID 的逻辑。这样可以避免重复的 ID,提高组件的可重用性和可维护性。

5. useReducer

useReducer():Action钩子。在使用React的过程中,如遇到状态管理,一般会用到Redux。而React本身是不提供状态管理的。而useReducer() 提供了状态管理

首先,关于redux我们都知道,其原理是通过用户在页面中发起action,从而通过reducer方法来改变state,从而实现页面和状态的通信。

而Reducer的形式是(state, action) => newstate。hooks的形式如下:

useReducer接收三个参数,第一个参数是处理状态更新的reducer,第二个参数是状态初始值,第三个参数是状态初始化函数(如果传递第三个参数,就会将第二个参数作为第三个参数的参数传递到函数中,返回的结果作为useReducer默认的初始值)

语法格式:

const [state, dispatch] = useReducer(reducer, initialState,initFn)

参数、返回值说明:

它接受 reducer函数状态的初始值 作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数

例如:使用useReducer()实现一个计数器

import  { useReducer } from "react";
const HookReducer = ()=> {
  const reducer = (state,action)=> {
    if (action.type === 'add') {
      return {
        ...state,
        count: state.count + 1
      }
    }else {
      return state
    }
  }
  const addCount = ()=> {
    dispatch({
      type: 'add'
    })
  }
  const [state,dispatch ] = useReducer(reducer,{count: 0})
  return (
    <>
      <p>{state.count}</p>
      <button onClick={ addCount }>useReducer</button>
    </>
  )
}
export default HookReducer;

通过代码可以看到,使用useReducer()代替了Redux的功能,但useReducer无法提供中间件等功能,假如有这些需求,还是需要用到redux。

6. useCallback

useCallback 是 React 提供的一个非常有用的 Hook,它的主要作用是“记住”一个函数,以避免在组件重新渲染时不必要地重新创建该函数。我们一点一点来理解这个概念,确保你完全明白。

6.1. 1. 为什么需要 useCallback

在 React 中,每次组件重新渲染时,组件内定义的所有函数都会重新创建。这在某些情况下可能会导致性能问题,特别是当你将这些函数传递给子组件时。

6.1.1. 举个例子:

假设我们有一个父组件,里面有一个按钮和一个子组件:

javascript
复制代码
import React, { useState } from 'react';

function ChildComponent({ onClick }) {
  console.log('子组件渲染了');
  return <button onClick={onClick}>点击我</button>;
}

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

  const handleClick = () => {
    console.log('按钮被点击了');
    setCount(count + 1);
  };

  return (
    <div>
    <p>计数: {count}</p>
    <ChildComponent onClick={handleClick} />
    </div>
  );
}

每次点击按钮,ParentComponent 组件都会重新渲染,因为 setCount 触发了状态变化。由于重新渲染,handleClick 这个函数会被重新创建。虽然这个例子看起来没什么问题,但当你在真实项目中,组件变得复杂时,这种重复创建函数的行为可能会导致性能问题。

6.2. 2. useCallback 是什么?

useCallback 可以帮助我们“记住”这个函数,确保它只有在依赖项发生变化时才会重新创建。

6.2.1. 还是刚才的例子:
javascript
复制代码
import React, { useState, useCallback } from 'react';

function ChildComponent({ onClick }) {
  console.log('子组件渲染了');
  return <button onClick={onClick}>点击我</button>;
}

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

  const handleClick = useCallback(() => {
    console.log('按钮被点击了');
    setCount(count + 1);
  }, [count]); // 只有当 count 变化时,才会重新创建 handleClick 函数

  return (
    <div>
    <p>计数: {count}</p>
    <ChildComponent onClick={handleClick} />
    </div>
  );
}

在这个例子中,我们使用 useCallback 包裹了 handleClick 函数。这样,handleClick 函数只有在 count 发生变化时才会被重新创建,而不会在每次重新渲染时都被重新创建。

6.3. 3. useCallback 的工作原理

  • 依赖项数组useCallback 接受一个依赖项数组,只有当数组中的依赖项发生变化时,它才会返回一个新的函数。这意味着,只要依赖项没有变化,useCallback 会一直返回之前记住的那个函数。

在上面的例子中,counthandleClick 的依赖项。如果 count 没有变化,handleClick 不会被重新创建。

6.4. 4. 使用场景

useCallback 通常在以下几种情况下使用:

  • 子组件使用了 React.memo:如果你使用 React.memo 来优化子组件,useCallback 可以确保你传递给子组件的回调函数只有在必要时才会更新,从而避免子组件不必要的重新渲染。
  • 避免不必要的函数创建:当你希望避免在每次渲染时都创建一个新的函数时,可以使用 useCallback

6.5. 5. 注意事项

  • 依赖项必须正确:如果在依赖项数组中遗漏了某个依赖项,可能会导致你的函数无法正确更新,导致潜在的 bug。因此,确保依赖项数组中包含了所有在回调函数中使用的状态或 props。
  • 不要滥用 useCallback:虽然 useCallback 可以优化性能,但如果不小心使用,反而可能让你的代码变得复杂且难以维护。通常,只有在性能确实有影响时才考虑使用它。

6.6. 总结

  • useCallback 可以记住一个函数的引用,只有在它的依赖项发生变化时才会重新创建这个函数,从而避免了不必要的函数重新创建。
  • 这在你传递函数给子组件,或者需要优化性能时非常有用。

7. useMemo

useMemo 是 React 中的一个 Hook,用于优化性能,特别是在计算开销比较大的操作时。它的作用是“记住”计算的结果,只有在依赖项发生变化时才重新计算。让我一步步地解释 useMemo,确保你能理解。

7.1. 1. 为什么需要 useMemo

在 React 中,每次组件重新渲染时,所有代码都会重新执行。如果某些计算比较复杂,或者数据量大,频繁的重新计算会影响性能。useMemo 可以帮助我们“记住”上次计算的结果,避免不必要的重复计算。

7.2. 2. useMemo 是什么?

useMemo 是一个 Hook,它接收一个“创建函数”和一个依赖项数组。只有当依赖项发生变化时,useMemo 才会重新计算创建函数的结果,否则会返回之前记住的结果。

7.3. 3. 使用 useMemo 的场景

  • 性能优化:当你有一些计算量比较大的操作,希望避免在每次渲染时都重新计算,可以使用 useMemo
  • 避免不必要的重新渲染:有时候子组件可能依赖父组件的计算结果,使用 useMemo 可以避免在父组件重新渲染时子组件的重复渲染。

7.4. 4. 如何使用 useMemo

让我们通过一个简单的例子来理解 useMemo

javascript
复制代码
import React, { useState, useMemo } from 'react';

function ParentComponent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  // 假设这个函数非常耗时
  const expensiveCalculation = (num) => {
    console.log('计算中...');
    for (let i = 0; i < 1000000000; i++) {} // 模拟一个非常耗时的操作
    return num * 2;
  };

  // 使用 useMemo 记住计算结果
  const calculatedValue = useMemo(() => expensiveCalculation(count), [count]);

  return (
    <div>
    <h1>计数: {count}</h1>
    <p>计算结果: {calculatedValue}</p>
    <button onClick={() => setCount(count + 1)}>增加计数</button>
  <input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="输入一些文本"
  />
  </div>
);
}

export default ParentComponent;
7.4.1. 在这个例子中:
  1. expensiveCalculation 是一个耗时的计算:这个函数模拟了一个非常耗时的操作(通过一个大循环),返回 count * 2 的结果。
  2. useMemo 用于记住计算结果useMemo 会记住 expensiveCalculation 的结果,并且只有在 count 改变时才会重新计算。如果 count 没有变化,useMemo 会直接返回之前的结果,而不需要再次计算。
  3. 性能优化:由于 useMemo,即使组件重新渲染,如果 count 没有变化,耗时的计算也不会再次执行,从而提升了性能。

7.5. 5. 什么时候使用 useMemo

  • 当你有 复杂的计算逻辑,并且这个计算依赖于某些状态时。
  • 当你想避免子组件因为父组件不必要的重新计算而 重新渲染

7.6. 6. useMemo 的工作原理

  • 依赖项数组useMemo 的第二个参数是依赖项数组。只有当数组中的依赖项发生变化时,useMemo 才会重新计算值。否则,它会返回之前记住的计算结果。
  • 性能提升:通过记住计算结果,useMemo 可以显著减少不必要的计算操作,提升性能。

7.7. 7. 注意事项

  • 不要滥用 useMemo:虽然 useMemo 可以优化性能,但如果滥用它,反而会让代码变得复杂且难以维护。通常,只有在计算开销确实比较大时才需要使用 useMemo
  • 依赖项必须正确:确保依赖项数组中包含所有你在计算中使用的状态或 props,否则可能导致错误的结果。

7.8. 总结

  • useMemo 记住一个计算结果,只有当依赖项发生变化时才会重新计算,目的是避免不必要的重复计算,提升性能。
  • 在有复杂计算或者不希望组件因为无关因素重新渲染时,使用 useMemo 是非常有用的。

希望这个解释对你有帮助!如果你有其他问题,或者还有不明白的地方,随时问我。

8. memo

1. 什么是 React.memo

React.memo 是一个高阶组件(HOC),用于优化函数组件的性能。

1.1 问题背景

在 React 中,每当父组件重新渲染时,子组件也会默认重新渲染,即使它们的 props(属性)没有改变。这样可能会导致不必要的重新渲染,影响性能。

1.2 React.memo 的作用

React.memo 用于包裹一个函数组件,使其只在 props 发生变化时才重新渲染。这样就避免了不必要的渲染,提高了性能。

1.3 使用方式
import React from 'react';

// 原始的函数组件
function MyComponent(props) {
  console.log("组件渲染了!");
  return <div>{props.name}</div>;
}

// 使用 React.memo 包裹组件
const MemoizedComponent = React.memo(MyComponent);

// 现在 MemoizedComponent 只有在 props.name 改变时才会重新渲染
1.4 工作原理

React.memo 会对传递给组件的 props 进行浅比较(浅比较下面会有小知识点)(即简单地比较 props 的值),如果值没有变化,它就会跳过渲染,直接使用上一次的渲染结果。

浅&深 比较知识点
1. 什么是浅比较?

浅比较(Shallow Comparison)指的是只比较对象或数组的第一层属性,而不深入检查对象内部的嵌套结构或数组中的子元素。

1.1 简单数据类型的比较

对于简单数据类型(如数字、字符串、布尔值等),浅比较和我们平时的比较是一样的。

javascript
复制代码
let a = 5;
let b = 5;
console.log(a === b);  // true

let x = "hello";
let y = "hello";
console.log(x === y);  // true

在以上例子中,ab 进行的是值比较。因为它们的值相等,结果是 true

1.2 对象和数组的浅比较

当比较对象和数组时,浅比较只会检查第一层属性,也就是属性的引用是否相同。

javascript
复制代码
let obj1 = { name: "Alice" };
let obj2 = { name: "Alice" };
console.log(obj1 === obj2);  // false

在这个例子中,obj1obj2 虽然看起来内容相同,但它们是两个不同的对象,存储在内存中的不同位置,因此结果是 false

1.3 引用类型的浅比较

对于对象和数组,浅比较通常只比较它们的引用,即它们在内存中的地址是否相同。

javascript
复制代码
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
console.log(arr1 === arr2);  // false

let arr3 = arr1;
console.log(arr1 === arr3);  // true
  • arr1arr2 虽然有相同的元素,但它们是两个不同的数组,存储在不同的内存地址,因此比较结果为 false
  • arr3arr1 的引用(即指向相同的内存地址),所以它们的比较结果为 true
2. 为什么需要浅比较?

浅比较通常用于性能优化,例如在 React 中使用 React.memo 时。

当你使用 React.memo 包裹一个组件时,React 会对传入组件的 props 进行浅比较。如果 props 没有变化(即引用没有变化),React 就不会重新渲染这个组件,从而提升性能。

3. 浅比较的局限性

浅比较有一个明显的局限性,就是它无法正确比较包含嵌套对象或数组的情况。

javascript
复制代码
let obj1 = { user: { name: "Alice" } };
let obj2 = { user: { name: "Alice" } };
console.log(obj1 === obj2);  // false

在这个例子中,obj1obj2 虽然结构和内容都一样,但浅比较只会比较它们的引用,而不会深入比较它们的嵌套内容。因此结果是 false

4. 深比较

与浅比较相对的是深比较(Deep Comparison),它会递归地检查对象或数组的每一层结构,以判断它们是否真正相等。

javascript
复制代码
function deepEqual(obj1, obj2) {
    if (obj1 === obj2) return true;

    if (typeof obj1 === "object" && obj1 !== null &&
        typeof obj2 === "object" && obj2 !== null) {
        
        let keys1 = Object.keys(obj1);
        let keys2 = Object.keys(obj2);
        
        if (keys1.length !== keys2.length) return false;
        
        for (let key of keys1) {
            if (!deepEqual(obj1[key], obj2[key])) return false;
        }
        
        return true;
    }
    
    return false;
}

let obj1 = { user: { name: "Alice" } };
let obj2 = { user: { name: "Alice" } };
console.log(deepEqual(obj1, obj2));  // true

上面的 deepEqual 函数就是一个简单的深比较的实现,它能够递归地比较对象或数组的每一层结构。

5. 总结
  • 浅比较:只比较对象或数组的第一层引用,适用于性能优化的场景。
  • 深比较:递归比较对象或数组的每一层结构,确保两个复杂数据类型的内容完全相同。

浅比较非常高效,适合在需要快速比较的场景中使用,比如 React 中的 React.memo。深比较虽然更精确,但性能开销也更大,适合在需要确保数据完全相等的场景中使用。

9. useRef

useRef 是 React 提供的一个 Hook,它有很多用途,主要用于访问 DOM 元素或保存不需要引发重新渲染的变量。接下来,我会一步步解释 useRef,让你理解它的作用和使用场景。

1. useRef 是什么?

useRef 是一个 Hook,返回一个可变的 ref 对象。这个对象有一个 .current 属性,你可以通过这个属性访问或修改它存储的值。

2. useRef 的主要用途

  • 访问 DOM 元素useRef 常用于直接访问和操作 DOM 元素,类似于传统的 JavaScript 中的 document.getElementByIddocument.querySelector
  • 保存组件中的值useRef 可以用于保存某个值,在组件的生命周期中保持不变。最重要的是,修改这个值不会导致组件重新渲染。

3. useRef 的基本用法

访问 DOM 元素

有时我们需要直接操作某个 DOM 元素,比如聚焦一个输入框。useRef 使得在函数组件中可以很容易地做到这一点。

javascript
复制代码
import React, { useRef } from 'react';

function FocusInput() {
  const inputRef = useRef(null); // 创建一个 ref 对象

  const handleFocus = () => {
    inputRef.current.focus(); // 直接访问 DOM 元素并调用其 focus 方法
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="点击按钮聚焦" />
      <button onClick={handleFocus}>聚焦输入框</button>
    </div>
  );
}

export default FocusInput;
在这个例子中:
  • useRef(null) 创建了一个 ref 对象,inputRef
  • ref={inputRef} 将这个 ref 绑定到 input 元素上,意味着 inputRef.current 现在指向这个 input 元素。
  • inputRef.current.focus() 通过 inputRef.current 直接访问 input 元素,并调用其 focus 方法。

4. 保存不需要触发重新渲染的值

有时,我们想要在组件的生命周期中保持某个值,但不希望这个值的变化引发组件重新渲染。这时候也可以使用 useRef

javascript
复制代码
import React, { useRef, useState } from 'react';

function Timer() {
  const [count, setCount] = useState(0);
  const timerRef = useRef(null); // 用于保存计时器的 ID

  const startTimer = () => {
    timerRef.current = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);
  };

  const stopTimer = () => {
    clearInterval(timerRef.current);
  };

  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={startTimer}>开始计时</button>
      <button onClick={stopTimer}>停止计时</button>
    </div>
  );
}

export default Timer;
在这个例子中:
  • timerRef 被用来保存 setInterval 返回的计时器 ID。
  • 当计时器开始运行时,timerRef.current 保存了计时器的 ID。
  • 当你停止计时器时,clearInterval(timerRef.current) 清除了这个计时器。

5. useRefuseState 的区别

  • useState:当你使用 useState 时,每次你更新状态,组件都会重新渲染。这非常适合保存那些需要反映在 UI 上的数据。
  • useRef:当你使用 useRef 时,更新 .current 的值不会引发组件重新渲染。它非常适合保存那些不需要显示在 UI 上、也不应该触发重新渲染的值。

6. 注意事项

  • useRef 并不会触发重新渲染:如果你更新 ref 对象的 .current 属性,这个更新不会触发组件的重新渲染。
  • useRef 返回的对象是可变的useRef 返回的 ref 对象是可变的,这意味着你可以随时修改其 .current 属性而不会影响 React 的渲染流程。

7. 总结

  • useRef 可以用来访问 DOM 元素,或者保存不需要触发重新渲染的变量。
  • useRef 返回的对象包含一个 .current 属性,你可以通过它来访问或修改存储的值。
  • 使用 useRef 可以帮助你在函数组件中执行某些需要持久化或者直接操作 DOM 的任务,而不会干扰组件的渲染逻辑。

10. useDeferredValue

useDeferredValue 是一个 React Hook,可以让你延迟更新 UI 的某些部分。

11. useDeferredValue

const deferredValue = useDeferredValue(value)

12. useDebugValue

13. useImperativeHandle

  • 25
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值