React从Class方式转Hooks详解

目录
  • React Hooks
    • 前言
    • Why Hooks ?
      • For Class
      • For Function
      • Class & Hooks 对比
    • Hooks如何保存组件状态和使用生命周期?
      • 1、useState:让函数具有维持状态的能力
      • 2、useEffect:执行副作用
  • 总结

React Hooks

前言

之前工作三年中一直在用class方式去写前端功能页面这些,其实接触hooks也是有一定时间了。在第一次接触的时候应该是看了一门关于electron+react的项目的课程的时候。当时主要是去看electron,所以对hooks没有特别的关注。也有可能是长期以来对class的熟悉,当时对hooks这种函数式的写法也有一些抵触。但是由于业内对hooks的好评,曾经也想去用hooks去起一个项目,但是由于当时项目周期以及已有的技术栈等原因,一直想实践也没机会去实践使用。

由于,最近接手新的项目一直使用的是react hooks+ts这一套。于是,就得开始使用hooks了。其实就功能开发而言,照猫画虎,其实工作也没有什么问题的。但是由于一直没有系统的对hooks进行深入一步的了解,导致在很多时候,其实并不是很清楚为什么要这样去使用,于是最近去找了《React Hooks核心原理与实战》去进行了学习。

在使用层面以及原因层面上,重新审视hooks的使用以及为什么要使用hooks。函数式写法究竟有哪些好处进行了更进一步的思考。其实,在一定程度而言,也还是浅尝辄止,这里仅仅将我这段时间学习记录下来。

Why Hooks ?

Hooks很大的一个亮点是可以进行业务逻辑的重用。这一点在hooks中体现的尤为明显。比如,往常的class中如果要去监听窗口大小的变化的时候,就得在组件中在挂载后去添加监听事件,但是如果当另外一个地方需要用到这种监听窗口大小功能的话,这种逻辑代码并不可以复用,只能在那个组件中重新写一遍。但是在hooks中,我们可以将这部分监听的逻辑代码进行hooks方式封装,完全可以做到逻辑上的复用。

For Class

  • 使用Class作为React的载体的时候:
  • 组件之间不会相互继承,没有利用到class的继承的特性UI是状态驱动的,所有的方法都是内部调用或者作为生命周期的方法内部自动调用。没有使用到类的实例方法可以调用的特性

For Function

React中的一个核心就是要实现从State数据到View试图层面的一个绑定。使用函数,其实更好的去解决State到View的一个映射问题。但是,用函数作为React的载体就会出现两个问题,函数中状态的保存以及生命周期的方法

  • Hooks怎样去解决上面两个问题:把一个外部的数据绑定到函数的执行。当数据变化时,让函数能够自动重新执行。这样的话,任何会影响 UI 展现的外部数据,都可以通过这个机制绑定到 React 的函数组件。
  • Hooks钩子的理解:把某个目标结果钩到某个可能会变化的数据源或者事件源上,那么当被钩到的数据或事件发生变化时,产生这个目标结果的代码会重新执行,产生更新后的结果

图解:一个执行过程(Execution),例如是函数组件本身,可以绑定在(钩在)传统意义的 State,或者 URL,甚至可以是窗口的大小。这样当 State、URL、窗口大小发生变化时,都会重新执行某个函数,产生更新后的结果。

Class & Hooks 对比

  • 比起 Class 组件,函数组件是更适合去表达 React 组件的执行的,因为它更符合 State => View 这样的一个逻辑关系。但是因为缺少状态、生命周期等机制,让它一直功能受限。Hooks解决了函数组件作为React载体的状态生命周期等受限问题,让其功能充分发挥出来
  • Hooks 中被钩的对象,可以是某个独立的数据源,也可以是另一个 Hook 执行的结果,这就带来了 Hooks 的最大好处:逻辑的复用
  • 简化了逻辑复用
    • Class方式中:使用高阶组件的设计模式进行逻辑复用。比如:我们要去复用一个窗口resize的功能,我们需要去定义一个没有UI的外层组件,去写相关resize的逻辑定义,然后将数据结果用属性的方式传给子组件。组件要复用这个逻辑的话,必须外层用这个组件包裹并返回。针对整个而言,**为了传递一个外部的状态,我们不得不定义一个没有 UI 的外层组件,而这个组件只是为了封装一段可重用的逻辑。**频繁使用,每一个高阶组件的使用都会多一层节点,会给调试等带来很大的负担。
//Class中高阶组件实现resize方法复用
//1、高阶组件的声明
const withWindowSize = Component => {
  // 产生一个高阶组件 WrappedComponent,只包含监听窗口大小的逻辑
  class WrappedComponent extends React.PureComponent {
    constructor(props) {
      super(props);
      this.state = {
        size: this.getSize()
      };
    }
    componentDidMount() {
      window.addEventListener("resize", this.handleResize);
    }
    componentWillUnmount() {
      window.removeEventListener("resize", this.handleResize);
    }
    getSize() {
      return window.innerWidth > 1000 ? "large" :"small";
    }
    handleResize = ()=> {
      const currentSize = this.getSize();
      this.setState({
        size: this.getSize()
      });
    }
    render() {
      // 将窗口大小传递给真正的业务逻辑组件
      return <Component size={this.state.size} />;
    }
  }
  return WrappedComponent;
};
//2、组件MyComponent使用高阶组件中的resize功能
class MyComponent extends React.Component{
  render() {
    const { size } = this.props;
    if (size === "small") return <SmallComponent />;
    else return <LargeComponent />;
  }
}
// 使用 withWindowSize 产生高阶组件,用于产生 size 属性传递给真正的业务组件
export default withWindowSize(MyComponent);
  • Hooks方式中:实现resize的话,窗口大小只是外部的一个数据状态。我们使用hooks方式对其封装,只是将其变成了一个可以绑定的数据源,当窗口大小发生变化的时候,这个组件也会重新渲染代码会更加简洁直观,并且不会产生额外的组件节点。
//Hooks中使用hooks方法进行resize逻辑复用
//定义useWindowSize这个hook
const getSize = () => {
  return window.innerWidth > 1000 ? "large" : "small";
}
const useWindowSize = () => {
  const [size, setSize] = useState(getSize());
  useEffect(() => {
  const handler = () => {
      setSize(getSize())
    };
    window.addEventListener('resize', handler);
    return () => {
      window.removeEventListener('resize', handler);
    };
  }, []);
  return size;
};
//函数组件中使用这个hook
const Demo = () => {
  const size = useWindowSize();
  if (size === "small") return <SmallComponent />;
  else return <LargeComponent />;
};
  • 有助于关注分离

Hooks能够让针对同一个业务逻辑的代码尽可能聚合在一块,在Class组件中不得不吧同一个业务逻辑代码分散在类组件的不同的生命周期方法中

图解:左侧是 Class 组件,右侧是函数组件结合 Hooks。蓝色和黄色代表不同的业务功能

Hooks如何保存组件状态和使用生命周期?

React一共提供了10个HooksuseStateuseEffectuseCallbackuseMemouseRefuseContext等等

1、useState:让函数具有维持状态的能力

我们要遵循的一个原则就是:state 中永远不要保存可以通过计算得到的值,例如:

  • 从 props 传递过来的值。有时候 props 传递过来的值无法直接使用,而是要通过一定的计算后再在 UI 上展示,比如说排序。那么我们要做的就是每次用的时候,都重新排序一下,或者利用某些 cache 机制,而不是将结果直接放到 state 里。
  • 从 URL 中读到的值。比如有时需要读取 URL 中的参数,把它作为组件的一部分状态。那么我们可以在每次需要用的时候从 URL 中读取,而不是读出来直接放到 state 里。
  • 从 cookie、localStorage 中读取的值。通常来说,也是每次要用的时候直接去读取,而不是读出来后放到 state 里。

2、useEffect:执行副作用

副作用是指一段和当前执行结果无关的代码。比如说要修改函数外部的某个变量,要发起一个请求。形式:useEffect(callback, dependencies)。涵盖了componentDidMountcomponentDidUpdatecomponentWillUnmount三个生命周期方法。简而言之,useEffect 是每次组件 render 完后判断依赖并执行。

使用useEffect应该注意的点:

没有依赖项,则每次 render 后都会重新执行

useEffect(()=>{
	console.log('re-render')		//每次render完成一次后就执行
})
  • 空数组作为依赖项,则只在首次执行时触发,对应到 Class 组件就是 componentDidMount
useEffect(()=>{
  console.log('did mount')		//相当于componentDidMount
},[])
  • 可以返回一个函数,用在组件销毁的时候做一些清理的操作
const [size,setResize] = useState({})
useEffect(()=>{
	const handler = () => {
    setResize()
	}
	window.addEventListener('resize',handler)
	return ()=>{
		window.removeEventListener('resize',handler)
	}
},[])

总结

  • useEffect使用的四个场景 每次 render 后执行:不提供第二个依赖项参数。比如useEffect(() => {})。
  • 仅第一次 render 后执行:提供一个空数组作为依赖项。比如useEffect(() => {}, [])。
  • 第一次以及依赖项发生变化后执行:提供依赖项数组。比如useEffect(() => {}, [deps])。
  • 组件 unmount 后执行:返回一个回调函数。比如useEffect() => { return () => {} }, [])。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • React 小技巧教你如何摆脱hooks依赖烦恼

    react项目中,很常见的一个场景: const [watchValue, setWatchValue] = useState(''); const [otherValue1, setOtherValue1] = useState(''); const [otherValue2, setOtherValue2] = useState(''); useEffect(() => { doSomething(otherValue1, otherValue2); }, [watchValue, othe

  • 30分钟带你全面了解React Hooks

    概述 1. Hooks 只能在函数组件内使用: 2. Hooks 用于扩充函数组件的功能,使函数组件可以完全代替类组件 React Hooks 都挂在 React 对象上,因此使用时为 React.useState() 的形式,若嫌麻烦,可以提前导入,如下: import React, { useState } from "react" React 内置的 Hooks 有很多,这里介绍一些常用到的.全部的请看 Hooks API 用到了 Hook 的函数组件名必须首字母大写,否则会被

  • React Hooks使用常见的坑

    React Hooks 是 React 16.8 引入的新特性,允许我们在不使用 Class 的前提下使用 state 和其他特性.React Hooks 要解决的问题是状态共享,是继 render-props 和 higher-order components 之后的第三种状态逻辑复用方案,不会产生 JSX 嵌套地狱问题. 为什么会有Hooks? 介绍Hooks之前,首先要给大家说一下React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好

  • React hooks的优缺点详解

    前言 Hook 是 React 16.8 的新增特性.它是完全可选的,并且100%向后兼容.它可以让你使用函数组件的方式,运用类组件以及 react 其他的一些特性,比如管理状态.生命周期钩子等.从概念上讲,React 组件一直更像是函数.而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则. 优点: 1.代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便阅读维护.例如,每个生

  • React之Hooks详解

    目录 什么是钩子(hooks) 类组件 函数组件 为什么创造Hooks 总结 什么是钩子(hooks) 消息处理的一种方法, 用来监视指定程序 函数组件中需要处理副作用,可以用钩子把外部代码"钩"进来 常用钩子:useState, useEffect, useContext, useReducer Hooks一律使用use前缀命名:useXXX 类组件 函数组件 一类特殊的函数,为你的函数式组件注入特殊的功能 为什么创造Hooks 有些类组件冗长且复杂,难以复用 结局方案:无状态组件与

  • 详解React Hooks是如何工作的

    1. React Hooks VS 纯函数 React Hook 说白了就是 React V18.6 新增的一些 API,API的本质就是提供某种功能的函数接口.因此,React Hooks 就是一些函数,但是 React Hooks 不是纯函数. 什么是纯函数呢?就是此函数在相同的输入值时,需产生相同的输出,并且此函数不能影响到外面的数据. 简单理解就是函数里面不能用到在外面定义的变量,因为如果用到了外面定义的变量,当外面的变量改变时会影响函数内部的计算,函数也会影响到外面的变量. 对于 Re

  • React从Class方式转Hooks详解

    目录 React Hooks 前言 Why Hooks ? For Class For Function Class & Hooks 对比 Hooks如何保存组件状态和使用生命周期? 1.useState:让函数具有维持状态的能力 2.useEffect:执行副作用 总结 React Hooks 前言 之前工作三年中一直在用class方式去写前端功能页面这些,其实接触hooks也是有一定时间了.在第一次接触的时候应该是看了一门关于electron+react的项目的课程的时候.当时主要是去看el

  • react后台系统最佳实践示例详解

    目录 一.中后台系统的技术栈选型 1. 要做什么 2. 要求 3. 技术栈怎么选 二.hooks时代状态管理库的选型 context redux recoil zustand MobX 三.hooks的使用问题与解决方案 总结 一.中后台系统的技术栈选型 本文主要讲三块内容:中后台系统的技术栈选型.hooks时代状态管理库的选型以及hooks的使用问题与解决方案. 1. 要做什么 我们的目标是搭建一个适用于公司内部中后台系统的前端项目最佳实践. 2. 要求 由于业务需求比较多,一名开发人员需要负

  • react中的ajax封装实例详解

    react中的ajax封装实例详解 代码块 **opts: {'可选参数'} **method: 请求方式:GET/POST,默认值:'GET'; **url: 发送请求的地址, 默认值: 当前页地址; **data: string,json; **async: 是否异步:true/false,默认值:true; **cache: 是否缓存:true/false,默认值:true; **contentType: HTTP头信息,默认值:'application/x-www-form-urlenc

  • react国际化化插件react-i18n-auto使用详解

    react-i18n-auto专门为中文国际化提供的自动化方案,快速迭代国际化开发,方法如下 安装 npm install react-i18n-auto --save-dev 第一步:添加babel插件配置(.babelrc添加方式) { "plugins": [ "@babel/plugin-transform-runtime", "react-i18n-auto", "..." ] } 第二步:添加自动化配置 i18n.

  • react redux及redux持久化示例详解

    目录 一.react-redux 二.redux持久化 一.react-redux react-redux依赖于redux工作. 运行安装命令:npm i react-redux: 使用: 将Provider套在入口组件处,并且将自己的store传进去: import FilmRouter from './FilmRouter/index' import {Provider} from 'react-redux' import store from './FilmRouter/views/red

  • react编写可编辑标题示例详解

    目录 需求 初始需求 方案设计 方案一 span + contentEditable 思路 代码如下 在这个方案中遇到的问题 存在的问题 方案二 直接用input处理展示和编辑 踩到的坑 需求 因为自己换工作到了新公司,上周入职,以前没有使用过react框架,虽然前面有学习过react,但是并没有实践经验 这个需求最终的效果是和石墨标题修改实现一样的效果 初始需求 文案支持可编辑 用户点击位置即光标定位处 超过50字读的时候,超出部分进行截断 当用户把所有内容删除时,失去焦点时文案设置为 “无文

  • React Context源码实现原理详解

    目录 什么是 Context Context 使用示例 createContext Context 的设计非常特别 useContext useContext 相关源码 debugger 查看调用栈 什么是 Context 目前来看 Context 是一个非常强大但是很多时候不会直接使用的 api.大多数项目不会直接使用 createContext 然后向下面传递数据,而是采用第三方库(react-redux). 想想项目中是不是经常会用到 @connect(...)(Comp) 以及 <Pro

  • React Native系列之Recyclerlistview使用详解

    目录 recyclerlistview的介绍与使用 1.安装 2.概述和功能 3. RecyclerListView的使用 1.dataProvider 2.LayoutProvider 3.rowRenderer 4.onEndReached 5.onEndReachedThreshold 6.extendedState 7.scrollViewProps RecyclerListView所有属性 recyclerlistview的介绍与使用 1.安装 npm install --save r

  • React高阶组件使用教程详解

    目录 高阶组件(HOC) 概述 使用HOC解决横切关注点问题 不用改变原始组件使用组合 约定-将不相关的 props 传递给被包裹的组件 约定-最大化可组合性 约定-包装显示名称以便轻松调试 使用高阶组件的注意事项 高阶组件(HOC) 概述 是React复用组件逻辑的一种高级技巧,是一种基于React组合特性而形成的设计模式 高阶组件是参数为组件,返回值为新组件的函数 简单理解: 高阶组件本身是 函数,传参数是组件,返回值也是组件: 高阶组件不用关心数据是如何渲染的,只用关心逻辑即可 被包装的组

  • React中useLayoutEffect钩子使用场景详解

    目录 简介 useEffect钩子的概述 钩子流程 useLayoutEffect钩子的概述 钩子流程 什么时候使用useLayoutEffect钩子? 总结 简介 不久前,React对其功能组件进行了一次重大更新(在2019年3月的16.8版本中),终于为这些组件提供了一种变得有状态的方法. 钩子的加入不仅意味着功能组件将能够提供自己的状态,而且还能通过引入useEffect钩子来管理自己的生命周期事件. 此外,这次更新还引入了一个全新的useLayoutEffect钩子,根据React文档,

随机推荐