首页 > 软件 > Hooks允许我们使用函数调用来定义组件的状态逻辑吗

Hooks允许我们使用函数调用来定义组件的状态逻辑吗

软件 2024-07-03

Hooks概念理解

本节任务: 能够理解hooks的概念及解决的问题
1. 什么是hooks
Hooks的本质:一套能够使函数组件更强大,更灵活的“钩子”
React体系里组件分为 类组件 和 函数组件
经过多年的实战,函数组件是一个更加匹配React的设计理念 UI = f(data),也更有利于逻辑拆分与重用的组件表达形式,而先前的函数组件是不可以有自己的状态的,为了能让函数组件可以拥有自己的状态,所以从react v16.8开始,Hooks应运而生
注意点:
有了hooks之后,为了兼容老版本,class类组件并没有被移除,俩者都可以使用
有了hooks之后,不能在把函数成为无状态组件了,因为hooks为函数组件提供了状态
hooks只能在函数组件中使用
2. Hooks解决了什么问题
Hooks的出现解决了俩个问题 1. 组件的状态逻辑复用 2.class组件自身的问题
组件的逻辑复用
在hooks出现之前,react先后尝试了 mixins混入,HOC高阶组件,render-props等模式
但是都有各自的问题,比如mixin的数据来源不清晰,高阶组件的嵌套问题等等
class组件自身的问题
class组件就像一个厚重的‘战舰’ 一样,大而全,提供了很多东西,有不可忽视的学习成本,比如各种生命周期,this指向问题等等,而我们更多时候需要的是一个轻快灵活的'快艇'

如何写自定义 React Hook?

大家好,我是在学习 React 的前端西瓜哥。
我们在写 React 函数组件时,如果想要复用组件的部分逻辑,可以考虑写自定义 Hook。本文会教大家如何写自定义 React Hook。
在此之前,我们先了解一下 Hook 的使用规则。
首先 Hook 只能在函数组件的顶层使用,不能在循环、条件、嵌套函数中执行。
这和 React Hook 的实现原理有关。当第一次执行函数组件时,React 会分配一个对象,然后一个个调用 Hook 时,将传入的参数或得到的结果依次放入到有序的表中缓存起来,然后保存在该对象中。
之后再次执行函数组件时,必须要保证这些 Hook 的执行顺序相同,才能做依赖项的对比,以及和前一次渲染的状态的对比等对比逻辑。
如果能 hook 可以出现循环、条件、嵌套函数中,就不能保证 Hook 执行顺序不变。
此外Hook 只能在函数组件内工作,不要在非组件函数中使用 Hook 。
如果你在普通函数内使用了 Hook,React 会报错。
当然如果是自定义 Hook(一个使用了 React 内置 Hook 的普通函数),然后放到函数组件内,那也是合法的。
为防止开发者不小心写岔,React 官方还写了一个名为 eslint-plugin-react-hooks 的 ESLint 插件,用于检测不合法的 Hook 调用位置,实在是太贴心了。强烈建议使用。
自定义 Hook,就是使用了内置 Hook 的普通函数。它是对函数组件可复用的部分逻辑的做了一层封装,然后再被函数组件使用。
自定义的 Hook 必须使用 use 开头 。因为 React 的官方 ESLint 插件认为 use 开头是自定义 Hook。
如果你不用 use 开头,ESLint 插件就会认为这是一个普通函数,调用时不符合 Hook 规则时不会报错,你就可能写出有问题的代码。
另外,自定义 Hook 下使用的 Hook,也必须位于顶层,这也是为了保证 hook 的顺序多次执行能保持一致。
下面我们来写几个常用的自定义 Hook。
我们希望实现类似类函数 componentDidMount 的效果。
使用方式:
相比原来的 useEffect 的实现,做了封装后的 useMount 更语义化一些。此外也不需要写多余的依赖项,并将组件销毁的回调函数也隔离出去了。
类似类函数 componentWillUnmount 的 useUnmount Hook 实现如下:
下面我们再实现一个 useEffect 的剔除掉挂载那一次的版本,对标类函数的 componentDidUpdate。
思路其实很简单,就是多使用一个默认值为 true 的布尔值变量。我们用它来记录当前是否为第一次执行,如果是,就不执行传入的函数,然后将布尔值设置为 false。
false 代表之后都是第二次第三次的执行,每次都执行传入的函数。
这里我们没有用 useState,因为通过 setState 方法会重新渲染组件。所以我们使用了 useRef,修改它的值不会触发组件的更新。
自定义 Hook,是一种比组件更小粒度的可复用逻辑组织方式,这也是 React 函数组件带给我们最大的惊喜。为此,我们有必要学习好自定义 Hook。

React Hook

react 16.8 以后加上了 react hook,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。16.8之前,react组件可以分为类组件和函数组件。
我们为什么要拥抱react hook?由于类组件存在以下几点问题:
下面逐一介绍官方提供的hook API。
1.useState()
作用:返回一个状态以及能修改这个状态的setter,在其他语言称为元组(tuple),一旦mount之后只能通过这个setter修改这个状态。
2.useEffect(callback, arr)
作用:处理函数组件中的副作用,如异步操作、延迟操作等。useEffect有两个参数,callback和数组依赖项,无arr时相当于componentDidMount生命周期,有arr时相当componentDidMount和componentDidUpdata生命周期。如果callback中有return,则相当于componentWillUnmount。
3.useContext
作用:跨组件共享数据钩子,使用可分为三步:
4.useReducer
作用:用于管理复杂的数据结构(useState一般用于管理扁平结构的状态),基本实现了redux的核心功能。 useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
5.useMemo、useCallback
这俩个Api与性能优化有关。react中,性能的优化点在于:
基于上面的两点,我们通常的解决方案是:使用immutable进行比较,在不相等的时候调用setState;在shouldComponentUpdate中判断前后的props和state,如果没有变化,则返回false来阻止更新。
在hooks出来之后,我们能够使用function的形式来创建包含内部state的组件。但是,使用function的形式,失去了上面的shouldComponentUpdate,我们无法通过判断前后状态来决定是否更新。而且,在函数组件中,react不再区分mount和update两个状态,这意味着函数组件的每一次调用都会执行其内部的所有逻辑,那么会带来较大的性能损耗。因此useMemo 和useCallback就是解决性能问题的杀手锏。
useCallback和useMemo的参数跟useEffect一致。useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。
通过一个例子来看useMemo的作用:
不使用useMemo,无论count还是val改变都会执行expensive()
使用useMemo,只有在count发生改变使才会会执行expensive()
useCallback跟useMemo比较类似,但是使用场景不同:比如有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。
6.useRef
useRef是一个方法,返回一个可变的 ref 对象;其 .current 属性被初始化为传入的参数(initialValue);可以保存任何类型的值:dom、对象等任何可变值;返回的 ref 对象在组件的整个生命周期内保持不变;修改 ref 的值是不会引发组件的重新 render 。
useRef非常常用的一个操作,访问DOM节点,对DOM进行操作,监听事件等等,如下:
除了传统的用法之外,它还可以“跨渲染周期”保存数据。
在上面的例子中,点击了 打印like 按钮后,连续点击数字按钮,会发现2s后 likeRef.current 打印出11,而 like 打印出1。
因为,在任意一次渲染中,props和 state 是始终保持不变的,如果props和state在任意不同渲染中是相互独立的话,那么使用到他们的任何值也是独立的。所以onButtonClick时拿到的时未点击数字按钮时的 like 值。
而ref 在所有 render 都保持着唯一的引用,因此所有的对 ref 的赋值或者取值拿到的都是一个 最终 的状态,而不会存在隔离。
7.useImperativeHandle
当userRef用于一个组件时,useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值;如果不使用,父组件的ref(chidlRef)访问不到任何值(childRef.current==null);且 useImperativeHandle 应当与 forwardRef 一起使用。
8.useLayoutEffect
用法与useEffect 相同,但它会在所有的 DOM 变更之后同步调用(立即执行)。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。由于会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制。
使用场景:当useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现闪屏问题。
9.useDebugValue
useDebugValue 用于在 React 开发者工具中显示 自定义 Hook 的标签。
useDebugValue 接受一个格式化函数作为可选的第二个参数。该函数只有在 Hook 被检查时才会被调用。它接受 debug 值作为参数,并且会返回一个格式化的显示值。

HOOK—useState、useEffect的使用

HOOK是React的新增特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。下面主要介绍一下useState和useEffect 的使用。
通过在函数组件里调用它来给组件添加一些内部 state。useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。useState 唯一的参数就是初始 state。这个初始 state 参数只有在第一次渲染的时候会被用到。
使用useState可以声明多个state变量
useEffect (副作用函数)是一个 Effect Hook,给函数组件增加了操作副作用(在 React 组件中进行数据获取、订阅或者手动修改 DOM等)的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API,默认情况下,React 会在每次渲染后调用副作用函数 ,包括第一次渲染的时候。
通过使用 Hook,你可以把组件内相关的副作用组织在一起(例如创建订阅及取消订阅),而不要把它们拆分到不同的生命周期函数里。
上面实例每次重新渲染都要执行一遍useEffect,如果你在useEffect中使用了useState则会导致无限循环,这样显然是影响性能的。为了处理这个问题,我们可以给useEffect传第二个参数。用第二个参数来告诉react只有当这个参数的值发生改变时,才执行我们传的副作用函数(即第一个参数)。
当我们第二个参数传一个空数组[]时,相当于只在首次渲染的时候执行。
在这里只是简单的介绍了HOOK中useState和useEffect的使用,其实HOOK特性还有很多内容值得我们去学习去探索。

React Hooks完全上手指南

hooks 是 函数组件独有的 。在不编写 class 的情况下使用 state 以及其他的 React 特性
只能在函数组件的顶级作用域使用;只能在函数组件或者其他 Hooks 中使用 。
hooks 使用时必须确保
使用 Hooks 的一些特性和要遵循某些规则。
React 官方提供了一个 ESlint 插件,专门用来检查 Hooks 是否正确被使用。
useState 的入参也可以是一个函数。当入参是一个函数的时候,这个函数只会在这个组件初始化渲染的时候执行
setState 也可以接收一个函数作为参数: setSomeState(prevState => {})
useEffect 会在每次 DOM 渲染后执行,不会阻塞页面渲染。
在页面更新后才会执行 。即:每次组件 render 后,判断依赖并执行。
它同时具备 componentDidMount 、 componentDidUpdate 和 componentWillUnmount 三个生命周期函数的执行时机。
useEffect 共两个参数: callback 和 dependences 。规则如下
依赖项一般是一个常量数组,而不是一个变量。因为一般在创建 callback 的时候,你其实非常清楚其中要用到哪些依赖项了。
React 会使用 浅比较 来对比依赖项是否发生了变化,所以要 特别注意数组或者对象类型 。
如果你是每次创建一个新对象,即使和之前的值是等价的,也会被认为是依赖项发生了变化。这是一个刚开始使用 Hooks 时很容易导致 Bug 的地方。
useLayoutEffect 的用法跟 useEffect 的用法是完全一样的,它们之间的唯一区别就是执行的时机
useLayoutEffect 会阻塞页面的渲染 。会保证在页面渲染前执行,也就是说页面渲染出来的是最终的结果。
如果使用 useEffect ,页面很可能因为渲染了2次而出现抖动
绝大多数情况下,使用 useEffect 即可
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。
useReducer 用法跟 Redux 非常相似,当 state 的计算逻辑比较复杂又或者需要根据以前的值来计算时,使用这种 Hook 比 useState 会更好
结合 context API,我们可以模拟 Redux 的操作
useCallback 和 useMemo 设计的初衷是用来做性能优化的
useCallback 缓存的是 方法的引用
useMemo 缓存的则是 方法的返回值
使用场景是 减少不必要的子组件渲染
若要实现 class 组件的 shouldComponentUpdate 方法,可以使用 React.memo 方法。
区别是它只能比较 props ,不会比较 state
useRef 值的变化不会引起组件重绘,可以存一些跟界面显示无关的变量
函数式组件不能设置 ref ,想保存其中的某些值,可以通过 React.forwardRef
自定义 Hook 是一个函数,但是 名称必须是以 use 开头 ,函数内部可以调用其他的 Hook
自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性
跟普通的 hook 一样, 只能在函数组件或者其他 Hooks 中使用

标签:前端开发 信息技术 未分类 外语学习 编程语言

大明白知识网 Copyright © 2020-2022 www.wangpan131.com. Some Rights Reserved. 京ICP备11019930号-18