重学React之hooks

简介: 重学React之hooks

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。


class组件相对于函数式组件有什么优势


状态的保存


  • class组件可以定义自己的state,用来保存组件自己内部的状态。


  • 函数式组件不可以,因为函数每次调用都会产生新的临时变量;


生命周期


  • class组件有自己的生命周期,我们可以在对应的生命周期中完成自己的逻辑。比如在componentDidMount中发送网络请求,并且该生命周期函数只会执行一次。


  • 函数式组件在学习hooks之前,如果在函数中发送网络请求,意味着每次重新渲染都会重新发送一次网络请求。


执行时机


  • class组件可以在状态改变时只会重新执行render函数以及我们希望重新调用的生命周期函数componentDidUpdate等。


  • 函数式组件在重新渲染时,整个函数都会被执行,似乎没有什么地方可以只让它们调用一次。 所以,在Hook出现之前,对于上面这些情况我们通常都会编写class组件。


class组件存在的问题


复杂组件变得难以理解


  • 我们在最初编写一个class组件时,往往逻辑比较简单,并不会非常复杂。但是随着业务的增多,我们的class组件会变得越来越复杂。比如componentDidMount中,可能就会包含大量的逻辑代码:包括网络请求、一些事件的监听(还需要在componentWillUnmount中移除)。


  • 而对于这样的class实际上非常难以拆分:因为它们的逻辑往往混在一起,强行拆分反而会造成过度设计,增加代码的复杂度。


组件复用状态很难


  • 为了一些状态的复用我们需要通过高阶组件或render props。


  • Provider、Consumer来共享一些状态,但是多次使用Consumer时,我们的代码就会存在很多嵌套。 这些代码让我们不管是编写和设计上来说,都变得非常困难。


useState


  • 返回一个数组,其中包含两个值,一个是state, 另一个是修改state值的函数。


  • 该函数类似 class 组件的 this.setState但是它不会把新的 state 和旧的 state 进行合并。而是替换原来的值。 如果我们修改数据,我们就必须传入一个新的值。React中会进行新旧值的对比,发现相同就不会再次渲染组件,如果不相同才会渲染组件。


import React, { useState } from 'react'
function UseEffectTest() {
  const [friends, setFriends] = useState(['zh', 'zhh'])
  // push方式
  const pushWay = () => {
    friends.push('llm')
    setFriends(friends)
  }
  // 新传入一个数组
  const newWay = () => {
    setFriends([...friends, 'llm'])
  }
  return (
    <div>
      <p>朋友</p>
      <ul>
        {friends.map((item, index) => {
          return <li key={index}>{item}</li>
        })}
      </ul>
      <button
        onClick={(e) => {
          pushWay()
        }}
      >
        添加朋友push方式
      </button>
      <button onClick={newWay}>添加朋友新传入一个数组方式</button>
    </div>
  )
}


网络异常,图片无法展示
|


  • useState 唯一的参数就是初始 state。这个初始 state 参数只有在第一次渲染时会被用到。


useEffect


  • 在这个hook中,我们可以写一些副作用逻辑。默认情况下,只要组件重新渲染,它就会执行。


  • 它跟 class 组件中的 componentDidMountcomponentDidUpdatecomponentWillUnmount 具有相同的用途,只不过被合并成了一个 API。


  • React 在完成对 DOM 的更改后运行你的“副作用”函数。


  • 副作用函数还可以通过返回一个函数来指定如何“清除”副作用。 类似于class组件的componentWillUnmount。默认情况下,每次触发时,都先执行清除操作。如果需要指定执行的依赖,我们可以传入一个数组作为userEffect的第二个参数。


  • 通过使用该 Hook,你可以把组件内相关的副作用组织在一起(例如创建订阅及取消订阅),而不要把它们拆分到不同的生命周期函数里。


  • 在该函数指定之前,dom已经完成了更新。


  • 如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 useEffect。


useContext


  • 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。


  • 当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider>value prop 决定。


  • 当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。


  • 这个方法只是为了获取context值,而提供context值,依旧是使用原来的方法。先调用createContext来创建一个context对象,然后利用这个context对象的Provider组件提供value值。


useReducer


  • 他只是useState的替代方案。而不是我们认为的redux。


  • 他接受三个参数。第一个参数为reducer函数,第二个参数为state的初始值,第三个参数为一个函数,可以惰性地创建初始 state。


  • 如果 Reducer Hook 的返回值与当前 state 相同,React 将跳过子组件的渲染及副作用的执行。


const initialState = {count: 0};
    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, initialState);
      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
        </>
      );
    }


useCallback


  • 传入一个函数和一个数组,该数组主要是收集useCallback是否返回一个新的memoized回调函数的依赖项。


  • useCallback(fn, deps) 相当于 useMemo(() => fn, deps)


  • 主要的用途就是将函数传递给子组件时,做性能优化。当子组件继承自PureComponent或者传入memo高阶组件时,他是非常有必要的。如果useCallback传入的依赖项未发生改变,那么useCallback将不会返回一个新的memoized回调函数,那么子组件将不会被重新渲染。


useMemo


  • 我们知道,只要是改变state,props都会重新执行组件渲染,如果一个开销非常大的计算,但是我们改变的是其他的state,该开销并没有依赖这个改变的state。由于组件会被重新渲染,所以大开销计算也会被重新执行一遍,这是没有必要的。所以我们就可以通过useMemo来做性能优化。


网络异常,图片无法展示
|


  • 把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。


  • 如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。


useRef


  • useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数。


  • 他的绑定规律和以前的ref一样。不能使用在函数式组件中,需要使用forwardRef包裹。


  • 如果useRef中存储其他类型的值。那么在组件的整个生命周期中,他是不会变化的。


import React, { useState, useRef } from 'react'
    function UseEffectTest() {
      const [count, setCount] = useState(0)
      // useRef
      const numRef = useRef(count)// 一直都是0
      return (
        <div>
          <h1>ref: {numRef.current}</h1>
          <h1>count: {count}</h1>
          <button onClick={() => setCount(count + 1)}>Click me</button>
        </div>
      )
    }


  • 变更 .current 属性不会引发组件重新渲染。所以我们可以利用这一特性保存以前的状态值。


useImperativeHandle


  • useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。


  • useImperativeHandle 应当与forwardRef 一起使用。


  • 他的目的是选择性的暴露子组件实例的内容,不想让全部都暴露给父组件。


function FancyInput(props, ref) {
      const inputRef = useRef();
      // 只暴露focus方法。
      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));
      return <input ref={inputRef} ... />;
    }
    FancyInput = forwardRef(FancyInput);


useLayoutEffect


useLayoutEffect看起来和useEffect非常的相似,事实上他们也只有一点区别而已:


  • useEffect会在渲染的内容更新到DOM上后执行,不会阻塞DOM的更新。


  • useLayoutEffect会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新。 如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect。


自定义Hooks


  • 自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。


  • 主要的目的就是复用相同的逻辑。


Hook 使用规则


Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:


  • 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。


  • 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。


相关文章
|
1月前
|
前端开发 JavaScript 开发者
深入理解React Hooks:提升前端开发效率的关键
【10月更文挑战第5天】深入理解React Hooks:提升前端开发效率的关键
|
1月前
|
前端开发 JavaScript
React Hooks 全面解析
【10月更文挑战第11天】React Hooks 是 React 16.8 引入的新特性,允许在函数组件中使用状态和其他 React 特性,简化了状态管理和生命周期管理。本文从基础概念入手,详细介绍了 `useState` 和 `useEffect` 的用法,探讨了常见问题和易错点,并提供了代码示例。通过学习本文,你将更好地理解和使用 Hooks,提升开发效率。
68 4
|
1月前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
1月前
|
前端开发 JavaScript API
探索React Hooks:前端开发的革命性工具
【10月更文挑战第5天】探索React Hooks:前端开发的革命性工具
|
13天前
|
前端开发 JavaScript
深入探索React Hooks:从useState到useEffect
深入探索React Hooks:从useState到useEffect
|
23天前
|
前端开发 JavaScript 开发者
“揭秘React Hooks的神秘面纱:如何掌握这些改变游戏规则的超能力以打造无敌前端应用”
【10月更文挑战第25天】React Hooks 自 2018 年推出以来,已成为 React 功能组件的重要组成部分。本文全面解析了 React Hooks 的核心概念,包括 `useState` 和 `useEffect` 的使用方法,并提供了最佳实践,如避免过度使用 Hooks、保持 Hooks 调用顺序一致、使用 `useReducer` 管理复杂状态逻辑、自定义 Hooks 封装复用逻辑等,帮助开发者更高效地使用 Hooks,构建健壮且易于维护的 React 应用。
30 2
|
28天前
|
前端开发 开发者
React 提供的其他重要 Hooks
【10月更文挑战第20天】React 提供了一系列强大的 Hooks,除了 `useRef` 之外,还有许多其他重要的 Hooks,它们共同构成了函数式组件开发的基础。
35 6
|
5天前
|
缓存 前端开发 开发者
深入理解React Hooks,打造高效响应式UI
深入理解React Hooks,打造高效响应式UI
13 0
|
1月前
|
前端开发 JavaScript 开发者
React Hooks
10月更文挑战第13天
35 1
|
1月前
|
前端开发