React Hooks

2023-11-16

Facebook团队对社区上的MVC框架都不太满意的情况下,开发了一套开源的前端框架react,于2013年发布第一个版本。

react最开始倡导函数式编程,使用function以及内部方法React.creactClass创建组件,之后在ES6推出之后,使用类组件Class构建包含生命周期的组件。 

React 16.8是一个比较重要的版本,它主要新增了Hooks这一特性,改变了React中组件的写法方式。

Hooks:

Hooks是React 16.8中最重要的新增特性。Hooks本质上就是一类特殊的函数,它们可以为你的函数型组件(function component)注入一些特殊的功能,让您在不编写类的情况下使用 state(状态) 和其他 React 特性。使用Hooks,可以更方便地使用状态和副作用,同时也可以避免类组件中的一些问题。

React Hooks的出现让函数组件和类组件之间的差距变得更小,使得我们在开发React应用时更加灵活、简便。同时,React Hooks还可以帮助我们更好地组织和复用组件逻辑。

为什么要使用 React Hooks

  • 状态逻辑难以复用: 业务变得复杂之后,组件之间共享状态变得频繁,组件复用和状态逻辑管理就变得十分复杂。使用redux也会加大项目的复杂度和体积。
  • 组成复杂难以维护: 复杂的组件中有各种难以管理的状态和副作用,在同一个生命周期中你可能会因为不同情况写出各种不相关的逻辑,但实际上我们通常希望一个函数只做一件事情。
  • 类的this指向性问题: 我们用class来创建react组件时,为了保证this的指向正确,我们要经常写这样的代码:const that = this,或者是this.handleClick = this.handleClick.bind(this)>;一旦this使用错误,各种bug就随之而来。

为了解决这些麻烦,hooks 允许我们使用简单的特殊函数实现class的各种功能。

官方Hooks 

一:useContext:

useContext是React Hooks中的一种,它可以让我们在函数组件中使用Context。

Context是React中一种组件间通信的方式,可以让我们避免通过props层层传递数据,从而让组件间的通信更加简单和直接。使用useContext可以让我们更方便地使用Context,避免了类组件中使用Context时需要使用this.context的麻烦。

使用useContext需要传入一个Context对象,该对象可以通过React.createContext创建。一般来说,在应用中我们会提供一个Context,然后在需要使用该Context的组件中使用useContext获取对应的值。例如:


import React, { useContext } from 'react';
const MyContext = React.createContext({ name: 'default' });
function MyComponent() {
  const { name } = useContext(MyContext);
  return <div>{name}</div>;
}
function App() {
  return (
    <MyContext.Provider value={{ name: 'John' }}>
      <MyComponent />
    </MyContext.Provider>
  );
}

在上面的例子中,我们创建了一个名为MyContext的Context,并在App组件中提供了一个值{name: 'John'}。在MyComponent组件中,我们可以使用useContext(MyContext)获取到该值,并将其渲染到页面上。
使用useContext可以大大简化组件间通信的代码,同时也可以让我们更好地组织和复用组件逻辑。 

二:useReducer:

useReducer是React Hooks中的一种,它可以让我们在函数组件中使用Reducer。

Reducer是一种状态管理模式,常用于复杂应用的状态管理。使用Reducer可以让我们更好地组织和管理组件的状态。

使用useReducer需要传入一个Reducer函数和一个初始状态。Reducer函数接收当前状态和一个action对象,返回一个新的状态。当我们需要更新状态时,可以使用dispatch函数触发对应的action,从而更新状态。例如:

import React, { useReducer } from 'react';
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}
function Counter({ initialCount }) {
  const [state, dispatch] = useReducer(reducer, { count: initialCount });
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}
function App() {
  return <Counter initialCount={0} />;
}

在上面的例子中,我们创建了一个名为reducer的Reducer函数,它接收当前状态state和action对象,返回一个新的状态。我们还创建了一个名为Counter的组件,该组件使用useReducer来管理状态,并通过dispatch函数触发对应的action来更新状态。在App组件中,我们渲染了Counter组件,并传入了一个初始状态{ count: 0 }。

使用useReducer可以让我们更好地管理组件的状态,同时也可以更好地组织和复用组件逻辑。它和useState一样,都可以用于管理组件的状态,但是在复杂的场景下,useReducer可以更好地处理状态的变化。 

三:useEffect:

useEffect是React Hooks中的一种,它可以让我们在函数组件中处理副作用。

副作用指的是那些不属于组件渲染输出的,但是对组件渲染输出结果产生影响的操作,例如向服务器发送网络请求、访问浏览器缓存等操作

使用useEffect可以让我们更方便地处理这些副作用。
使用useEffect需要传入一个函数和一个依赖数组。该函数可以执行一些副作用操作,例如向服务器发送网络请求、访问浏览器缓存等等。当组件渲染到页面上时,useEffect会自动执行该函数。同时,如果依赖数组中的任何一个值发生了改变,useEffect也会重新执行该函数。如果依赖数组为空,useEffect只会在组件挂载和卸载时执行一次。例如:

import React, { useState, useEffect } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);
  return (
    <div>
      <p>You clicked {count} times</p >
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

在上面的例子中,我们创建了一个名为Counter的组件,并使用useState来管理状态。我们还使用useEffect来更新页面标题,当count的值发生改变时,useEffect会重新执行,从而更新页面标题。在Counter组件中,我们渲染了一个计数器和一个按钮,点击按钮可以让计数器递增。
使用useEffect可以让我们更方便地处理副作用,它和类组件中的componentDidMount、componentDidUpdate和componentWillUnmount方法类似,但是使用起来更加简单方便。同时,使用useEffect也可以让我们更好地遵循React的基本原则,即“将组件渲染输出和副作用分开处理”。

四:useCallback:

useCallback是React Hooks中的一种,它可以让我们更好地处理函数的性能问题。

在React中,每次状态改变或props改变时,函数组件会重新渲染,如果函数组件中的函数没有被优化,每次重新渲染都会导致函数的重新创建和重新分配内存,造成性能浪费。

使用useCallback可以让我们缓存函数,避免不必要的重新创建和分配内存。
使用useCallback需要传入一个函数和一个依赖数组。该函数会被缓存,只有依赖数组中的值发生改变时,才会重新创建该函数。例如:

import React, { useState, useCallback } from 'react';
function Counter() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  const handleClick = useCallback(() => {
    setCount1(count1 + 1);
  }, [count1]);
  return (
    <div>
      <p>Count1: {count1}</p >
      <p>Count2: {count2}</p >
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

在上面的例子中,我们创建了一个名为handleClick的回调函数,并使用useCallback缓存该函数。当count1的值发生改变时,该函数才会重新创建。在Counter组件中,我们渲染了两个计数器和一个按钮,点击按钮可以让count1递增。
使用useCallback可以让我们更好地处理函数的性能问题,避免不必要的重新创建和分配内存。它适用于那些需要频繁传递给子组件的回调函数,例如在列表中渲染子项时,需要给每个子项传递一个回调函数来处理点击事件。

五:useRef:

useRef是React Hooks中的一种,它可以在函数组件中使用ref。

ref通常用于获取组件或DOM节点的引用,或者用于在组件之间共享数据。使用useRef可以让我们更方便地在函数组件中使用ref。

使用useRef需要调用React.useRef()函数,并将其赋值给某个变量。该变量的current属性可以用于存储和访问ref的值。例如:

import React, { useRef } from 'react';
function TextInputWithFocusButton(props) {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </div>
  );
}

在上面的例子中,我们创建了一个名为inputEl的ref,并将其赋值给input元素的ref属性。我们还创建了一个名为onButtonClick的函数,该函数可以通过inputEl.current.focus()来让input元素获得焦点。在TextInputWithFocusButton组件中,我们渲染了一个input元素和一个按钮,点击该按钮可以让input元素获得焦点。
使用useRef可以让我们更方便地在函数组件中使用ref,避免了在类组件中使用ref时需要使用this.refs的麻烦。除了获取DOM节点的引用,useRef还可以用于在组件之间共享数据,因为它可以在组件之间保持数据的引用。 

六:useMemo:

useMemo是React Hooks中的一种,它可以让我们更好地处理计算密集型的操作,避免不必要的重新计算。

在React中,每次状态改变或props改变时,函数组件会重新渲染,如果组件中存在一些计算密集型的操作,每次重新渲染都会导致这些操作的重新计算,造成性能浪费。使用useMemo可以让我们缓存计算结果,避免不必要的重新计算。
使用useMemo需要传入一个计算函数和一个依赖数组。该计算函数会被缓存,只有依赖数组中的值发生改变时,才会重新计算。例如:

import React, { useState, useMemo } from 'react';
function Counter() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  const result = useMemo(() => {
    return count1 * count2;
  }, [count1, count2]);
  return (
    <div>
      <p>Result: {result}</p >
      <button onClick={() => setCount1(count1 + 1)}>Increment Count1</button>
      <button onClick={() => setCount2(count2 + 1)}>Increment Count2</button>
    </div>
  );
}

在上面的例子中,我们创建了一个名为result的变量,并使用useMemo缓存该变量。当count1或count2的值发生改变时,该变量才会重新计算。在Counter组件中,我们渲染了一个结果和两个按钮,点击按钮可以让count1或count2递增。
使用useMemo可以让我们更好地处理计算密集型的操作,避免不必要的重新计算。它适用于那些需要进行复杂计算或者需要大量数据处理的操作,例如在列表中渲染子项时,需要对子项进行复杂计算或者大量数据处理。

七:useLayoutEffect: 

useLayoutEffect是React Hooks中的一种,它和useEffect很类似,但它在视图更新之前同步执行,可以用来进行DOM操作或者进行一些需要同步执行的操作。

在React中,每次状态改变或props改变时,组件会重新渲染,如果组件中存在一些需要同步执行的操作,例如DOM操作,使用useLayoutEffect可以确保这些操作在视图更新之前执行。
使用useLayoutEffect需要传入一个副作用函数和一个依赖数组。该副作用函数会在DOM更新之前同步执行,可以进行DOM操作或者进行一些需要同步执行的操作,例如使用第三方库操作DOM或者进行一些测量操作。例如:

import React, { useState, useLayoutEffect } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  useLayoutEffect(() => {
    const title = document.querySelector('title');
    title.innerText = `Count: ${count}`;
  }, [count]);
  return (
    <div>
      <p>Count: {count}</p >
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
    </div>
  );
}

在上面的例子中,我们使用useLayoutEffect在DOM更新之前同步修改了页面的title,使其显示当前的count值。在Counter组件中,我们渲染了一个计数器和一个按钮,点击按钮可以让count递增。
使用useLayoutEffect可以确保一些操作在DOM更新之前同步执行,适用于那些需要同步执行的操作,例如DOM操作或者进行一些测量操作。但由于它在视图更新之前同步执行,所以需要注意性能问题,确保操作的执行时间不会太长,避免阻塞主线程。 

八:useImperativeHandle: 

 useImperativeHandle是React Hooks中的一种,它可以让我们在函数组件中访问子组件的实例方法或属性。

在React中,父组件可以通过ref访问子组件的实例,但是在函数组件中,我们无法通过ref访问子组件的实例。使用useImperativeHandle可以让我们在函数组件中定义子组件的实例方法或属性,并通过ref访问这些实例方法或属性。
使用useImperativeHandle需要传入一个ref对象和一个副作用函数。该副作用函数会返回一个对象,该对象中包含了子组件的实例方法或属性。例如:

import React, { useRef, useImperativeHandle } from 'react';
const Child = React.forwardRef((props, ref) => {
  const inputRef = useRef(null);
  useImperativeHandle(ref, () => ({
    focusInput: () => {
      inputRef.current.focus();
    }
  }));
  return (
    <div>
      <input type="text" ref={inputRef} />
    </div>
  );
});
function Parent() {
  const childRef = useRef(null);
  const handleClick = () => {
    childRef.current.focusInput();
  };
  return (
    <div>
      <Child ref={childRef} />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
}

在上面的例子中,我们定义了一个名为Child的子组件,并通过forwardRef向外暴露了一个ref。在Child组件中,我们使用useImperativeHandle定义了一个名为focusInput的实例方法,并将该方法作为对象返回。在Parent组件中,我们使用childRef访问了Child组件的实例,并在按钮的点击事件中调用了focusInput方法,使得input元素获取了焦点。
使用useImperativeHandle可以让我们在函数组件中访问子组件的实例方法或属性,但同时也需要注意一些性能问题,确保不会滥用该功能。

九:useReducer: 

useReducer是React Hooks中的一种,它可以让我们使用类似Redux的状态管理方式,将组件的状态转移到Reducer中,从而更好地管理组件的状态。

在React中,每次状态改变或props改变时,函数组件会重新渲染,如果组件中存在一些复杂的状态逻辑,使用useReducer可以更好地管理这些状态逻辑,使得代码更加清晰易懂。
使用useReducer需要传入一个reducer函数和一个初始状态。该reducer函数接受当前状态和一个action对象作为参数,并返回一个新的状态。action对象通常包含一个type属性和一些其他属性,用来描述状态的变化。例如:

import React, { useReducer } from 'react';
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}
function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  return (
    <div>
      <p>Count: {state.count}</p >
      <button onClick={() => dispatch({ type: 'increment' })}>Increment Count</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement Count</button>
    </div>
  );
}

在上面的例子中,我们创建了一个名为reducer的函数,用来描述计数器的状态变化。在Counter组件中,我们使用useReducer将组件的状态转移到reducer中,并使用dispatch函数来触发状态的变化。我们渲染了一个计数器和两个按钮,点击按钮可以让计数器的值递增或递减。
使用useReducer可以让我们更好地管理组件的状态,特别是那些复杂的状态逻辑,使得代码更加清晰易懂。它适用于那些需要进行状态管理的组件,例如表单、列表等。 

自定义Hooks

  • 自定义Hooks:可以封装状态,能够更好的实现状态共享。
  • 自定义hooks可以说成是一种约定而不是功能。
  • 当一个函数以use开头并且在函数内部调用其他hooks,那么这个函数就可以成为自定义hooks。

自定义Hooks是React中的一种编程模式,它可以让我们把组件中的逻辑抽象出来,从而使得我们可以在多个组件中复用该逻辑。自定义Hooks通常以use开头,并使用其他Hooks或JavaScript函数来实现某些功能。

例如,我们可以创建一个名为useFetch的自定义Hook,用来发送网络请求并返回数据。该Hook接受一个URL作为参数,并返回一个包含数据、错误和是否正在加载的状态。

该Hook使用了useEffect和useState等React Hooks来实现异步数据的获取和状态管理。

例如:

import { useState, useEffect } from 'react';
function useFetch(url) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
      } catch (error) {
        setError(error);
      } finally {
        setIsLoading(false);
      }
    }
    fetchData();
  }, [url]);
  return { data, error, isLoading };
}

在上面的例子中,我们创建了一个名为useFetch的自定义Hook,用来发送网络请求并返回数据。该Hook使用了useState和useEffect等React Hooks来管理数据、错误和加载状态,并在组件中使用该Hook来获取数据。

例如:

import React from 'react';
import useFetch from './useFetch';
function Post({ id }) {
  const { data, error, isLoading } = useFetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
  if (isLoading) {
    return <div>Loading...</div>;
  }
  if (error) {
    return <div>Error: {error.message}</div>;
  }
  return (
    <div>
      <h2>{data.title}</h2>
      <p>{data.body}</p >
    </div>
  );
}

在上面的例子中,我们使用了useFetch自定义Hook来获取单个博客文章的数据,并在组件中根据加载状态和错误状态进行渲染。该自定义Hook可以在其他组件中复用,从而实现了逻辑的复用和代码的重用。
自定义Hooks可以让我们把组件中的逻辑抽象出来,从而实现逻辑的复用和代码的重用。它适用于那些需要在多个组件中复用的逻辑或功能,例如网络请求、表单验证等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React Hooks 的相关文章

随机推荐

  • JAVA-注册成功后,通过多线程发送短信的使用场景

    场景 在日常的系统中 使用前的第一步往往是需要注册 只有注册后获得登录账号和密码才能正常使用系统 有些系统在注册成功后 还会往用户的手机发送一条注册成功的短信通知 这个并不属于主线流程 也就是说发不发送该短信通知 用户都已经能登录系统了 那
  • mapbox创建自定义marker图标绑定弹框展示信息

    创建自定义marker图标绑定弹框 let carMarker 存放marker图标 for let m 0 m
  • 怎么查看本地服务器信息,查看本地服务器url地址

    查看本地服务器url地址 内容精选 换一换 OBS Browser 是一款用于访问和管理对象存储服务的图形化工具 支持通过配置内网DNS服务器地址的方式 使在华为云上的Windows ECS通过内网直接访问OBS 下面将介绍具体其操作流程和
  • Android EditText的setOnEditorActionListener方法——监听软键盘按键

    文章目录 1 控件EditText的setOnEditorActionListener方法的使用 2 android 软件盘事件响应 android imeOptions KeyEvent android inputType 2 1 Edi
  • 修改NuGet下载包后存放的默认路径

    NuGet默认下完包后就放在C盘 我们给他改一下 NuGet的默认目录配置文件在C Users yourName AppData Roaming NuGet NuGet Config 加一下这个配置 我放在了D盘 你可以看情况自己改
  • 【Hadoop技术篇】hadoop的使用

    博主介绍 博主介绍 大家好 我是淼淼 喵 很高兴认识大家 主攻领域 大数据开发 数据仓库 ETL 数据分析 面试分析 点赞 评论 收藏 养成习惯 一键三连 欢迎关注 一起学习 一起讨论 一起进步 欢迎评论 作者水平有限 欢迎各位大佬指点 相
  • 沐神《动手学深度学习》报错 解决方案RuntimeError: DataLoader worker (pid(s) ...) exited unexpectedly

    3 6 softmax回归的从零开始实现 运行 print evaluate accuracy net test iter 报了一溜错误 其中最后一行为 RuntimeError DataLoader worker pid s 5052 1
  • 包和 jar 文件的创建

    前言 在之前的文章中 讲到了面向的 3 大特性 封装 继承 多态 和面向对象设计的 5 大原则 SRP OCP LSP DIP ISP 此外 我们还讲了如何创建一个类 并且在创建类后如何构造一个对象 然后还介绍了类中的属性和方法 并对构造方
  • 方程组线性化方法和牛顿迭代法基础

    方程组线性化方法和牛顿迭代法基础 非线性方程组线性化和牛顿迭代法 参考书籍 GPS原理与接收机设计 谢钢 非线性方程 就是因变量与自变量之间的关系不是线性的关系 这类方程很多 例如平方关系 对数关系 指数关系 三角函数等等 求解此类方程往往
  • ESP-NOW:竟然还有如此好用的无线通信方式

    用 Arduino 玩转 ESP32 系列历史文章目录 ESP32 概述与 Arduino 软件准备 ESP32 GPIO接口编程入门 蓝牙翻页笔 蓝牙键盘与PPT 控制器 B 站粉丝计数器 Siri 语音识别控制 LED 灯 Siri 语
  • eclipse 关于报错Error creating bean with name ‘xxxxx‘: Invocation of init method....

    网上查找的资料是这种异常一般都是包导错了 缺失 冲突 版本错误都可能 由于再报这个错之前我进行了添加删除修改pom xml文件 右键项目 gt maven gt update project project gt clean等这些对于项目整
  • C语言模拟实现atoi函数

    atoi函数 主要功能是将一个字符串转变为整数 例如将 12345 gt 12345 但在实现过程中 我们难免会因为考虑不够全面而漏掉比较重要的几点 今天就总结一下实现atoi函数需要注意的地方 1 指针为NULL 2 字符串为空字符串 3
  • etcd的简单使用

    etcd的简单使用 ETCD安装配置 安装 去https github com coreos etcd releases 下载想要的版本解压etcd包 解压后进入目录 增加x权限 chmod x etcd chmod x etcdctl 并
  • MySQL5.7安装报错:GPG key retrieval failed: [Errno 14] curl#37 - "Couldn't open file /etc/pki/rpm-gpg/RPM

    根据官方文档使用yum安装MySQL5 7 添加mysl comunity repo如下 mysql57 community name MySQL 5 7 Community Server baseurl http repo mysql c
  • [Docker]进入容器命令

    docker exec it api bin bash docker exec it api bin sh
  • JVM入门教程

    文章目录 简介 1 Java内存区域 1 1 程序计数器 1 2 Java虚拟机栈 1 3 本地方法栈 1 4 Java堆 1 5 方法区 1 6 运行时常量池 1 7 直接内存 2 HotSpot虚拟机 2 1 对象的创建 2 2 对象的
  • MyISAM和InnoDB区别关联详解

    Mysql架构 什么存储引擎 MySQL和InnoDB对比1 2 总结 Mysql存储架构 从上图可以发现 MySQL由以下几部分组成 连接池组件 管理服务和工具组件 SQL接口组件 查询分析器组件 优化器组件 缓冲 Cache 组件 插件
  • C++引用,四区和函数

    引用变量 四区 函数 没有函数重载 代码区 全局区 堆区和栈区 代码区 全局区 栈区 堆区 new操作符 引用 函数的默认参数 函数的占位参数 代码区 全局区 堆区和栈区 注意 其中代码区和全局区是运行前的 栈区和堆区是运行后的 即如果ex
  • 微信小程序实现举报功能

    一 后台接口 userController java 前端接收一个usersReportd对象 包含数据如下 PostMapping reportUser public IMoocJSONResult reportUser RequestB
  • React Hooks

    Facebook团队对社区上的MVC框架都不太满意的情况下 开发了一套开源的前端框架react 于2013年发布第一个版本 react最开始倡导函数式编程 使用function以及内部方法React creactClass创建组件 之后在E