三、react中类组件和函数组件

2023-11-19

简介

本篇我们只要介绍react中类组件与函数组件两种组件的写法、两者的优缺点;

同时对在我们的项目开发中该使用类组件还是函数组件进行思考分析;

废话不多说进入正题~

类组件

设计思路

类组件时面向对象编程的思想,在其中我们去设计类组件时使用state对象去完成组件内部数据的定义,结合react的生命周期构造(componentDidMount、componentDidUpdate、componentWillUnmount等等)完成整个组件的开发设计;

具体实现

示例

import React, {Component} from 'react';

class MyDemo extends Component {
  defaultProps = {
    test: 1,  // 如果没有给组件传test,那么props中test默认为1
  }
  constructor(props) {
    super(props);
    this.state = {
      name: '类组件',
      info: 'demo',
    }
  }
  componentDidMount () {
    console.log('我是一个生命周期函数,在完成首次渲染后调用');
    this.sayHello();
  }
  componentDidUpdate() {
    console.log('我是一个生命周期函数,在完成数据更新并渲染完成后调用')
  }
  componentWillUnmount() {
    console.log('我是一个生命周期函数,在组件销毁前调用');
  }
  sayHello () {
    console.log('hello!我是', this.state);
  }
  changeInfoValue () {
    this.setState({
      info: 'newDemo',
    },() => {
      console.log('state中的info属性值完成更新,变为', this.state.info);
    });
  }

  render() {
    return (
      <div onClick={changeInfoValue}>
        {this.state.name}
      </div>
    )
  }
}
export default MyDemo;

正如上面所示,便完成了我们一个类组件,当然其中还有一些其他的生命周期函数,我们在上一篇已经讲过了,不了解的小伙伴回到上一篇去看看,这里就不再介绍;

函数组件

设计模式

函数组件中没有 this,它提供了hook一系列方法来代替原先 类函数 中的一些特性,例如用useState创建状态变量,使用useEffect实现类似于componmentDidMount、shouldComponentUpdate等生命周期钩子函数的功能。

为啥都有类组件的模式了,还要推出函数组件呢

核心目的:为了降低复杂度、降低学习成本和理解成本;(这里并不是说类组件就不好啊~)

  • class也是有学习成本的,你必须去理解class中this的指向,其没有稳定的法语提案,但是对比而言函数就简单粗暴些了;
  • 要关注很多生命周期的状态这些,组件是一个套着一个,每个小组件中又有各个自己的生命周期,这就让一个复杂的组件有时候变得难以理解(当然这也和开发者自身的组件设计有关),函数组件则没有这些,hook提供的的方法在我看来对一下数据的状态管理更加可预测;

ps:react 并计划说要把类组件从react中移除,在一个项目中类组件和函数组件是可以共存的,facebokk就有一堆的类组件,但是他们并没有把他们全部重写成函数组件,在后续代码中他们会同时去使用 hook 和 class

什么是Hook

Hook是 react 在16.8后新加的特性,它能让我们不通过 class类的方式去写一个组件,也就是用函数的方式去写,它提供一系列的hook方法去代替state、生命周期等react特性;

ps:hook方法不能在类组件中去使用,一定写在函数组件的最外层,注意是最外层!!!

函数组件中有哪些Hook呢

useState

import { useState } from 'react';
const [state, setState] = useState(initialState);

返回一个 state,以及更新 state 的函数。

在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。

setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。

注意:如果你的更新值与当前 state 完全相同(React 使用 Object.is比较算法 来比较 state),则随后的重渲染会被完全跳过。

useEffect

import { useEffect } from 'react';
useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // 清除订阅
    subscription.unsubscribe();
  };
}, []);

在了解useEffect时你要知道几个概念:如何清除effect、effect的执行时机、如何控制effect的执行条件

  • 清除effect:在传入函数中的 return中加入一个函数,则这个函数就会在当组件卸载前执行;
  • effect的执行时机:执行时机与 componentDidMount、componentDidUpdate 不同的是,传给 useEffect 的函数会在浏览器完成布局与绘制之后,在一个延迟事件中被调用。
  • 增加effect的执行条件:可以给 useEffect 传递第二个参数,它是 effect 所依赖的值数组

注意

如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循输入数组的工作方式。

useContext

之前我们有讲过父子组件间传值的一种方式

先通过React.createContext()方式创建一个context,然后在函数内通过useContext()的方式返回context的当前值

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}

useReducer

当一个state有多个可以改变其的方法,或者说依赖了很多的其他state时,用useState显然通过setState去改state时需要用到很多的方法,写法繁杂,此时就可以用useReducer去代替它

useReduceruseState的平替方法

示例

const demo = () => {
  const initState = {count: 0};
  const reduce = (state, action) => {
    switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
  const [state, dispatch] = useReducer(reduce, initState);
    
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'reset', payload: initState})}>
        Reset
      </button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

ps:一般创建用到它比较少,但是如果我们用上它结合useContext就可以模拟实现redux的行为

useMemo

useMemo是用来缓存计算属性的;

计算属性其实是函数的返回值,或者说指那些以返回一个值为目标的函数。

有些函数,需要我们手动的去点击,去完成一些动作才触发。而有些函数,则是直接在渲染的时候就执行,在DOM区域被当作属性值一样去使用,这些就被称为计算属性。

有时候我们直接在dom中加入计算属性,那么每次渲染时它都会执行得到一个值,但是这个值明明每次执行得结果都一样,没必要每次都执行呀,这时候我们就可以用上useMemo,和vue中computer中定义方法差不多

const demo = () => {
  const [count, setCount] = useState(0);
  const doubleCount = useMemo(() => {
    return count*2;
  },[count])

  const newCount = useMemo(() => {
    let currentCount = 0
    for(let i=0;i<100000;++i){
      currentCount = currentCount + i + count;
    }
    return currentCount;
  },[count])
  return (
    <div>
      {doubleCount}
      {newCount}
    </div>
  )
}

ps:useMemo不是越多越好哈,缓存也是需要成本的,那我们时候需要优化呢?但计算量很大很复杂时用更合适,不要滥用,像上面例子中doubleCount没必要了用useMemonewCount就可以用useMemo

useCallback

它和useMemo的功能都做缓存用,只不过useCallback缓存的时函数,useMemo缓存的计算属性也就是值

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

ps:一样不能滥用,使用场景很少吧(个人理解)

useRef

一般我们在react组件中获取Dom有两个方法,一个是用传统的document,另外一个就是用useRef方法

const demoDom = () => {
  const input = useRef(null);
  const inputEl = useRef(null);
  const onButtonClick = () => {
    //这里的inputEl.current和document.getElementById('myInput')一样
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} id="myInput" type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

除了上面的hook,react还提供了useImperativeHandleuseLayoutEffectuseDebugValue这些hook方法,这里就不展开说了,可以去react官网看看

通过react提供的上面这些hook方法,我们就能模拟类函数中的生命周期函数,用函数的设计模式去写组件

总结

类组件和函数组件共同点

  • 无论是使用函数组件还是类组件,它都不能修改自己的 props 属性。
  • React是单向数据流,父组件改变了属性,那么子组件视图会更新。
  • 函数组件和类组件都可以接收 props 属性并且返回React元素。
  • 属性 props 是外界传递过来的,状态 state 是组件本身的,状态可以在组件中任意修改。

类组件和函数组件区别

当然函数组件和类组件是有区别的,而且函数组件比类组件性能好,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件(多写无状态组件,少写有状态组件)。

区别

类组件

函数组件

是否有this

没有

是否有生命周期

没有

是否有状态state

没有

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

三、react中类组件和函数组件 的相关文章

随机推荐

  • 华为OD机试真题- 任务混部

    题目描述 公司创新实验室正在研究如何最小化资源成本 最大化资源利用率 请你设计算法帮他们解决一个任务混部问题 有taskNum项任务 每个任务有开始时间 startTime 结束时间 endTime 并行度 parallelism 三个属性
  • LeetCode 剑指 Offer II 079. 所有子集

    给定一个整数数组 nums 数组中的元素 互不相同 返回该数组所有可能的子集 幂集 解集 不能 包含重复的子集 你可以按 任意顺序 返回解集 示例 1 输入 nums 1 2 3 输出 1 2 1 2 3 1 3 2 3 1 2 3 1 l
  • libgdx导入blender模型

    具体就是参考 官网 https libgdx com wiki graphics 3d importing blender models in libgdx blender 教程可以看八个案例教程带你从0到1入门blender 已完结 这里
  • 小米盒子打开adb调试模式

    1 先打开开发者模式 进入小米电视设置 gt 进入关于 gt 找到产品型号 gt 在产品型号上面连续多次按ok 确认 键 gt 然后就会提示 您已处于开发者模式 2 开启adb 经过第一步开启开发者模式之后 现在可以返回到设置页面 进入 账
  • stl库

    sort 读入n条学生成绩记录 包括学生姓名 总成绩 语文 数学和英语成绩 要求按总成绩从高到低输出 条记录 每条记录占一行 总成绩相同时按语文成绩从高到低输出 语文成绩相同时按数学成绩从高到低输出 没有两个人的成绩完全一样 include
  • 【JAVA】Abnormal build process termination: -Xmx700m -Djava.awt.headless=true -Djava.endorsed.d如何解决???

    Abnormal build process termination Xmx700m Djava awt headless true Djava endorsed dirs Djdt compiler u 如何解决 在尝试了n中网络上的方法
  • Vue中上传图片

    上传图片的两种方式 1 base64 上传 将图片转换成base64 然后再通过请求将base64上传到服务端 图片转换成base64很简单 直接百度就可以了 一大堆图片转base64的插件 但转换成的base64特别长 一般不建议使用 2
  • onnxruntime cuda版本使用时出现的错误汇总

    1 用qt c 推理 onnxruntime cuda时出现诸如 E onnxruntime barcode provider bridge ort cc 995 onnxruntime ProviderLibrary Get LoadLi
  • 程杰“大话设计模式”中的设计原则

    单一职责原则 SRP 就一个类而言 应该仅有一个引起它变化的原因 如果一个类承担的职责过多 就等于把这些职责耦合在了一起 一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力 这种耦合会导致脆弱的设计 当发生变化时 设计会遭受到意想不到
  • Python爬虫中如何通过post发请求,浏览器控制台抓包教程,有道翻译爬虫程序,通过python伪装翻译(post案例)

    目录 一 浏览器控制台抓包 1 打开方式以及常用选项 2 控制台NetWrok 二 Python爬虫中如何通过post发请求 1 Post请求 2 Python中使用post请求 三 有道翻译爬虫程序 通过python伪装翻译 post案例
  • Eclipse出现A project with this name already exists问题

    问题如图 我是由于删除时并没有删除完全造成的 解决 出现这种情况有可能是由于project命名冲突 但是可能自己并没发现有冲突的命名 有可能是以前命名过 但是没有完全删除以至于发生了冲突 可以在下图中的Navigator中查看是否已有此pr
  • 虚拟机上ubuntu-server的安装(详细完整版)

    一 安装 安装VMware 网上找Ubuntu的iso文件 桌面版的或者服务器版的都可以 Index of ubuntu releases 清华大学开源软件镜像站 Tsinghua Open Source Mirrorhttps mirro
  • 软件项目管理

    目录 前言 项目管理概述 项目与软件项目 项目管理与软件项目管理 项目管理知识体系 过程管理与软件项目管理的关系 软件项目管理过程 项目初始 项目确立 项目立项 项目招投标 项目授权 敏捷开发总结 scrum模型 迭代式增量软件开发过程 术
  • Apple:如何在iphone、ipad上安装一些常用命令行命令

    Apple 如何在iphone ipad上安装一些常用命令行命令 相信对Linux Unix比较熟悉的朋友 在iphone或 ipad越狱后发现通过Cydia可以安装OpenSSH 一定都想安装上并且通过ssh登录上去看看 但是登录后却发现
  • 推荐一些网络安全的网站和论坛

    今天小编给大家推荐一批网络安全的网站和论坛 想学习网络安全技术的朋友们可以去这些网站看看 1 红黑联盟 红黑联盟论坛 网址 bbs 2cto com 2 安全客 一个提供网络安全资讯的网站 网址 www anquanke com 3 T00
  • 【批处理DOS-CMD命令-汇总和小结】-CMD窗口的设置与操作命令-关闭cmd窗口、退出cmd环境(exit、exit /b、goto :eof)

    一 对exit命令和goto命令的基本认知 打印exit命令的帮助信息 执行命令 exit C Users Administrator gt exit 退出 CMD EXE 程序 命令解释器 或当前批处理脚本 EXIT B exitCode
  • php文件打印服务器,PHP打印到服务器端打印机

    我想用PHP打印到服务器端打印机 我发现了类似的示例代码 它们大多都使用相同的API函数来执行此任务 当我在我的服务器上运行它来测试它所说的代码时 PHP致命错误 调用未定义的函数printer open 所以我发现至少有三种不同版本的ph
  • keras.layers.Conv2D 与tf.layers.Conv2D 的兼容性: AttributeError: ‘tuple‘ object has no attribute ‘layer‘

    结论 keras layers Conv2D 与 tf layers Conv2D有相同的参数设置模式 keras layers Conv2D 可以兼容处理 tf layers Conv2D得到的tensor tf layers Conv2
  • 表示数值的字符串(含思路解答示意图)【剑指offer——JAVA实现】

    题目描述 请实现一个函数用来判断字符串是否表示数值 包括整数和小数 例如 字符串 100 5e2 123 3 1416 和 1E 16 都表示数值 但是 12e 1a3 14 1 2 3 5 和 12e 4 3 都不是 解法一 思路 状态机
  • 三、react中类组件和函数组件

    简介 本篇我们只要介绍react中类组件与函数组件两种组件的写法 两者的优缺点 同时对在我们的项目开发中该使用类组件还是函数组件进行思考分析 废话不多说进入正题 类组件 设计思路 类组件时面向对象编程的思想 在其中我们去设计类组件时使用st