React-Hook+ahooks的学习记录(持续更新)

简介

hook钩子可以极大加强开发效率,适用于react函数式组件,函数式组件没有生命周期于是用钩子函数来进行代替,钩子的功能十分强大,例如可以实现双向数据绑定等等的一系列操作,下面是我整理的钩子学习记录。参考官方文档

1、useCallback

useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。

const cachedFn = useCallback(fn, dependencies)

参数

  • fn:想要缓存的函数。此函数可以接受任何参数并且返回任何值。

  • dependencies:有关是否更新 fn 的所有响应式值的一个列表。响应式值包括 props、state,和所有在你组件内部直接声明的变量和函数。

返回值

在初次渲染时,useCallback 返回你已经传入的 fn 函数

在之后的渲染中, 如果依赖没有改变,useCallback 返回上一次渲染中缓存的 fn 函数;否则返回这一次渲染传入的 fn

用法

跳过组件的重新渲染,处理数据获取不变时的重复获取后进行渲染,处理出现闪屏等影响用户体验的问题

示例

import { useCallback, useState } from 'react';


const foo = () => {
  const [count, setCount] = useState(0);
  const handleClick = useCallback(() => {
    console.log('Clicked');
  }, [count]);

  function Child({ handleClick }:any) {
    console.log('Child component rendered');
    return <button onClick={handleClick}>Click Me</button>;
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <Child handleClick={handleClick} />
    </div>
  );
}

export default foo;

结果

 

注释

在上面的例子中,foo是一个函数组件,它有一个状态count和一个处理点击事件的函数handleClickhandleClick中的console.log语句只是一个示例,表示在真实场景中可能有更加复杂的操作。

Childfoo的子组件,它接收一个handleClick函数作为props并将其绑定到一个按钮的点击事件上。

当我们点击Increment按钮时,foo的状态count会递增,导致它重新渲染。但是由于handleClick是通过useCallback缓存的,它的函数实例在不变的情况下被重复使用,不会导致ChildComponent重新渲染。

如果我们没有使用useCallback,而是直接将一个内联函数传递给Child,那么每次foo重新渲染时,都会创建一个新的函数实例,导致Child重新渲染。

使用useCallback可以避免这种不必要的重新渲染,提高应用的性能。

2、useContext

useContext 是一个 React Hook,可以让你读取和订阅组件中的 context。

const value = useContext(SomeContext)

参数

  • SomeContext:先前用 createContext 创建的 context。context 本身不包含信息,它只代表你可以提供或从组件中读取的信息类型。

返回值

useContext 为调用组件返回 context 的值。它被确定为传递给树中调用组件上方最近的SomeContext.Providervalue。如果没有这样的 provider,那么返回值将会是为创建该 context 传递给 createContextdefaultValue。返回的值始终是最新的。如果 context 发生变化,React 会自动重新渲染读取 context 的组件。

用法

向组件树深层传递数据,通过订阅接收方式,简化了多个父子间传值的繁琐性

示例

import { createContext,useContext } from "react";


// 创建一个上下文对象
const MyContext = createContext('');

// 在上下文对象中提供默认值
const MyProvider = ({ children }:any) => {
  const value = "Hello from Context!";
  
  return (
    <MyContext.Provider value={value}>
      {children}
    </MyContext.Provider>
  );
};

// 使用useContext访问上下文中的值
const MyComponent = () => {
  const contextValue = useContext(MyContext);
  
  return <div>{contextValue}</div>;
};

// 在根组件中使用提供者组件
const App = () => {
  return (
    <MyProvider>
      <MyComponent />
    </MyProvider>
  );
};
export default App;

3、useDebugValue

useDebugValue 是一个 React Hook,可以让你在 React 开发工具 中为自定义 Hook 添加标签。由于作者不常用,因此不在赘述

4、useDeferredValue

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

const deferredValue = useDeferredValue(value)

参数

  • value:你想延迟的值,可以是任何类型。

返回值

在组件的初始渲染期间,返回的延迟值将与你提供的值相同。但是在组件更新时,React 将会先尝试使用旧值进行重新渲染(因此它将返回旧值),然后再在后台使用新值进行另一个重新渲染(这时它将返回更新后的值)。

用法

在新内容加载期间显示旧内容; 表明内容已过时 ;延迟渲染 UI 的某些部分 。

示例

结果

先输入SADASD,再退位到SAD,Data位置的数据出现延迟展示的效果

注释

在此示例中,我们使用useState来管理输入框中的值和从后台加载的数据。当用户输入时,我们使用useEffect设置一个定时器,模拟从后台加载数据。加载完成后,我们更新data的状态。

但是,我们希望在数据加载完成之前不渲染<p>元素。为了实现这一点,我们使用useDeferredValuedata的延迟版本赋给deferredData。这意味着deferredData将会在一段时间后更新为最新的值。

最后,我们将deferredData渲染到屏幕上,而不是直接渲染data。这样,即使数据加载完成之前,用户也不会看到旧的或不完整的数据。

通过使用useDeferredValue,我们可以优化渲染和用户体验,确保数据加载完成后再进行渲染,而不是使用未加载或不完整的数据。

5、useEffect

useEffect 是一个 React Hook,它允许你 将组件与外部系统同步。基本上是react函数式组件必用hook,官方文档上很清楚,不再赘述。

6、useId

useId 是一个 React Hook,可以生成传递给无障碍属性的唯一 ID。

const id = useId()

返回值

useId 返回一个唯一的字符串 ID,与此特定组件中的 useId 调用相关联。

7、useImperativeHandle

useImperativeHandle 是 React 中的一个 Hook,它能让你自定义由 ref 暴露出来的句柄。

useImperativeHandle(ref, createHandle, dependencies?)

参数

  • ref:该 ref 是你从 forwardRef 渲染函数 中获得的第二个参数。

  • createHandle:该函数无需参数,它返回你想要暴露的 ref 的句柄。该句柄可以包含任何类型。通常,你会返回一个包含你想暴露的方法的对象。

  • 可选的 dependencies:函数 createHandle 代码中所用到的所有反应式的值的列表。反应式的值包含 props、状态和其他所有直接在你组件体内声明的变量和函数。倘若你的代码检查器已 为 React 配置好,它会验证每一个反应式的值是否被正确指定为依赖项。该列表的长度必须是一个常数项,并且必须按照 [dep1, dep2, dep3] 的形式罗列各依赖项。React 会使用 Object.is 来比较每一个依赖项与其对应的之前值。如果一次重新渲染导致某些依赖项发生了改变,或你没有提供这个参数列表,你的函数 createHandle 将会被重新执行,而新生成的句柄则会被分配给 ref。

返回值

useImperativeHandle 返回 undefined

用法

向父组件暴露一个自定义的 ref 句柄 ;暴露你自己的命令式方法 

示例

import React, { useRef, useImperativeHandle, forwardRef } from 'react';

// 子组件
const ChildComponent = forwardRef((props, ref) => {
  const inputRef = useRef();

  // 暴露给父组件的方法
  useImperativeHandle(ref, () => ({
    focusInput: () => {
      inputRef.current.focus();
    },
    getValue: () => {
      return inputRef.current.value;
    }
  }));

  return (
    <input type="text" ref={inputRef} />
  );
});

// 父组件
const ParentComponent = () => {
  const childRef = useRef();

  const handleButtonClick = () => {
    childRef.current.focusInput();
    const value = childRef.current.getValue();
    alert(value);
  };

  return (
    <div>
      <ChildComponent ref={childRef} />
      <button onClick={handleButtonClick}>操作子组件</button>
    </div>
  );
};

export default ParentComponent;

结果

 注释

在上面的例子中,我们定义了一个子组件ChildComponent,其中包含一个输入框。我们使用useRef来创建一个inputRef引用,用于获取输入框的DOM元素。

然后,我们使用useImperativeHandle来定义子组件向父组件暴露的接口。在这个例子中,我们暴露了focusInputgetValue两个方法。focusInput方法用于将焦点聚焦到输入框,getValue方法用于获取输入框的值。

接下来,我们在父组件ParentComponent中使用useRef来创建一个childRef引用,用于获取子组件的实例。

最后,在handleButtonClick方法中,我们可以通过childRef.current来访问子组件的方法focusInputgetValue,从而直接操作子组件。

这样,当我们点击按钮时,子组件的输入框会聚焦,并且弹出框会显示输入框的值。

这就是useImperativeHandle的作用,它允许我们在使用React Hooks时,通过定义子组件向父组件暴露的接口,实现直接操作子组件的能力。

8、useInsertionEffect

useInsertionEffect 是为 CSS-in-JS 库的作者特意打造的。除非你正在使用 CSS-in-JS 库并且需要注入样式,否则你应该使用 useEffect 或者 useLayoutEffect。因此不在赘述。

9、useLayoutEffect

useLayoutEffect 可能会影响性能。尽可能使用 useEffectuseLayoutEffectuseEffect 的一个版本,在浏览器重新绘制屏幕之前触发。

10、useMemo

useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。

const cachedValue = useMemo(calculateValue, dependencies)

参数

  • calculateValue:要缓存计算值的函数。它应该是一个没有任何参数的纯函数,并且可以返回任意类型。React 将会在首次渲染时调用该函数;在之后的渲染中,如果 dependencies 没有发生变化,React 将直接返回相同值。否则,将会再次调用 calculateValue 并返回最新结果,然后缓存该结果以便下次重复使用。

  • dependencies:所有在 calculateValue 函数中使用的响应式变量组成的数组。响应式变量包括 props、state 和所有你直接在组件中定义的变量和函数。如果你在代码检查工具中 配置了 React,它将会确保每一个响应式数据都被正确地定义为依赖项。依赖项数组的长度必须是固定的并且必须写成 [dep1, dep2, dep3] 这种形式。React 使用 Object.is 将每个依赖项与其之前的值进行比较。

返回值

在初次渲染时,useMemo 返回不带参数调用 calculateValue 的结果。

在接下来的渲染中,如果依赖项没有发生改变,它将返回上次缓存的值;否则将再次调用 calculateValue,并返回最新结果。

用法

跳过代价昂贵的重新计算;跳过组件的重新渲染;记忆另一个 Hook 的依赖;记忆一个函数 

示例

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

const Example = () => {
  const [num1, setNum1] = useState(0);
  const [num2, setNum2] = useState(0);

  const sum = useMemo(() => {
    console.log('计算和');
    return num1 + num2;
  }, [num1, num2]);

  return (
    <div>
      <div>
        <label>数字1:</label>
        <input
          type="number"
          value={num1}
          onChange={(e) => setNum1(parseInt(e.target.value))}
        />
      </div>
      <div>
        <label>数字2:</label>
        <input
          type="number"
          value={num2}
          onChange={(e) => setNum2(parseInt(e.target.value))}
        />
      </div>
      <p>和:{sum}</p>
    </div>
  );
};

export default Example;

结果

先在数字1处输入01,值改变开始计算,再在数字2处输入00,值未变化,未计算返回原来的值

 

 注释

在上面的示例中,我们使用useState来创建两个状态变量num1和num2,分别表示输入的两个数字。然后,我们使用useMemo来缓存计算的和。useMemo接受一个依赖数组作为第二个参数,当依赖项发生变化时,才会重新计算和。在这个例子中,我们将num1和num2作为依赖项,只有当它们发生改变时,和的计算才会触发。

我们可以在组件中看到一个显示和的p元素。在每次num1或num2改变时,我们可以在控制台中看到"计算和"的日志。但是,如果我们只改变一个数字,另一个数字保持不变,那么和的计算将从缓存中获取,并且不会重新计算。

通过这个例子,我们可以验证useMemo的作用,它确实可以帮助我们缓存计算结果,并在依赖项没有改变时重复使用这些结果。这有助于提高React组件的性能。

11、useReducer

useReducer 是一个 React Hook,它允许你向组件里面添加一个 reducer

const [state, dispatch] = useReducer(reducer, initialArg, init?)

 参数

  • reducer:用于更新 state 的纯函数。参数为 state 和 action,返回值是更新后的 state。state 与 action 可以是任意合法值。
  • initialArg:用于初始化 state 的任意值。初始值的计算逻辑取决于接下来的 init 参数。
  • 可选参数 init:用于计算初始值的函数。如果存在,使用 init(initialArg) 的执行结果作为初始值,否则使用 initialArg

 返回值

useReducer 返回一个由两个值组成的数组:

  1. 当前的 state。初次渲染时,它是 init(initialArg) 或 initialArg (如果没有 init 函数)。
  2. dispatch 函数。用于更新 state 并触发组件的重新渲染。

用法

管理全局的状态,通过reducer可以在不同的组件获取到state里面的值,无需父子传值和组件订阅,数据在不同组件中传值次数过多的时候可以使用,减少代码繁琐性,方便管理state值,提升性能。

示例

//首先,我们需要在我们的组件中导入useReducer函数:
import React, { useReducer } from "react";

//然后,我们定义一个reducer函数,它将根据我们的操作类型来更新我们的计数状态:
const reducer = (state, action) => {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
};


//接下来,我们可以在我们的组件中使用useReducer来初始化我们的状态和动作分发函数。
const Counter = () => {
  const initialState = { count: 0 };
  const [state, dispatch] = useReducer(reducer, initialState);
  
  // 通过dispatch调用不同的操作类型来更新状态
  const increment = () => {
    dispatch({ type: "INCREMENT" });
  };

  const decrement = () => {
    dispatch({ type: "DECREMENT" });
  };

  return (
    <div>
      <h2>计数器</h2>
      <p>当前计数值: {state.count}</p>
      <button onClick={decrement}>减少</button>
      <button onClick={increment}>增加</button>
    </div>
  );
};

export default Counter;

结果

 注释

假设我们正在开发一个计数器应用程序,其中我们可以增加或减少计数值。在这个示例中,我们通过调用dispatch函数来分发不同的操作类型,从而更新我们的状态。使用useReducer,我们可以在一个地方集中处理所有操作,并根据当前状态返回新的状态。

12、useRef

useRef 是一个 React Hook,它能让你引用一个不需要渲染的值。常用于ref全局引用一个值,保证组件更新不会渲染;通过ref操作dom,避免重复创建ref内容,参考官方文档,不在赘述

const ref = useRef(initialValue)

13、useState

useState 是一个 React Hook,它允许你向组件添加一个 状态变量。参考官方文档,不在赘述

const [state, setState] = useState(initialState);

14、useSyncExternalStore

useSyncExternalStore 是一个让你订阅外部 store 的 React Hook。

const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

参数

  • subscribe:一个函数,接收一个单独的 callback 参数并把它订阅到 store 上。当 store 发生改变,它应当调用被提供的 callback。这会导致组件重新渲染。subscribe 函数会返回清除订阅的函数。

  • getSnapshot:一个函数,返回组件需要的 store 中的数据快照。在 store 不变的情况下,重复调用 getSnapshot 必须返回同一个值。如果 store 改变,并且返回值也不同了(用 Object.is 比较),React 就会重新渲染组件。

  • 可选 getServerSnapshot:一个函数,返回 store 中数据的初始快照。它只会在服务端渲染时,以及在客户端进行服务端渲染内容的 hydration 时被用到。快照在服务端与客户端之间必须相同,它通常是从服务端序列化并传到客户端的。如果你忽略此参数,在服务端渲染这个组件会抛出一个错误。

返回值

该 store 的当前快照,可以在你的渲染逻辑中使用。

用法

订阅外部 store;订阅浏览器 API ;把逻辑抽取到自定义 Hook;添加服务端渲染支持

示例

// 这是一个第三方 store 的例子,
// 你可能需要把它与 React 集成。

// 如果你的应用完全由 React 构建,
// 我们推荐使用 React state 替代。

let nextId = 0;
let todos = [{ id: nextId++, text: "Todo #1" }];
let listeners: any = [];

export const todosStore = {
  addTodo() {
    todos = [...todos, { id: nextId++, text: "Todo #" + nextId }];
    emitChange();
  },
  subscribe(listener: any) {
    listeners = [...listeners, listener];
    return () => {
      listeners = listeners.filter((l: any) => l !== listener);
    };
  },
  getSnapshot() {
    return todos;
  },
};

function emitChange() {
  for (let listener of listeners) {
    listener();
  }
}

import { useSyncExternalStore } from "react";

function TodosApp() {
  const todos = useSyncExternalStore(
    todosStore.subscribe,
    todosStore.getSnapshot
  );
  return (
    <>
      <button onClick={() => todosStore.addTodo()}>Add todo</button>
      <hr />
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
    </>
  );
}

export default TodosApp;

结果

点击add增加一条

注释

这是一个使用第三方store的例子。这个store用来管理todo列表。它有一个addTodo方法用来添加新的todo,一个subscribe方法用来订阅store的变化,一个getSnapshot方法用来获取当前的todo列表。在store发生变化时,会调用订阅的监听器函数来更新UI。

  1. 首先,定义一个nextId变量来跟踪下一个todo的id,以及一个todos数组来存储所有的todos。
  2. addTodo方法会在todos数组中添加一个新的todo对象,并自增nextId。然后调用emitChange函数来通知所有的监听器。
  3. subscribe方法接收一个监听器函数作为参数,并将其添加到listeners数组中。返回一个取消订阅的函数,可以用来在不需要监听的时候取消订阅。
  4. getSnapshot方法返回当前的todos数组。
  5. emitChange函数会遍历所有的监听器函数,并依次调用它们。
  6. 在使用该store的组件中,可以使用useSyncExternalStore hook来订阅store的变化并同步数据到组件中。
  7. 在TodosApp组件中,我们通过调用useSyncExternalStore并传入subscribe和getSnapshot方法来订阅store的变化并获取当前的todos数据。
  8. 在render函数中,我们渲染一个按钮来添加新的todo。当按钮被点击时,会调用todosStore.addTodo方法来添加新的todo。
  9. 接下来,我们将todos数组中的每个todo渲染成一个列表项。

 15、useTransition

useTransition 是一个让你在不阻塞 UI 的情况下来更新状态的 React Hook。

const [isPending, startTransition] = useTransition()

返回值

useTransition 返回一个由两个元素组成的数组:

  1. isPending 标志,告诉你是否存在待处理的转换。
  2. startTransition 函数 允许你将状态更新标记为转换状态。

ahooks(封装了许多钩子,提升开发效率,推荐使用)

安装

 npm install --save ahooks

官方文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值