React 组件与状态

2023-11-11

企业项目实战 > 第二部分 > React 基础回顾

React 组件与 State

  • 什么是组件

组件是什么?每个程序员都有自己的理解:在传统语言中, 组件的定义一般来说是一个从特定的组件类中派生出来的特定的对象;而在早期的前端开发者眼里, 组件是一个可复用的独立 UI 模块;在 React 中, 得益于 JSX 语法, 所有的页面元素都被转换成了 React 对象。只要你的方法 return 的是一个 React 元素, 小到一个 text, 大到一个 page, 都可以认为是 React 组件。
归纳起来一句话:如果一个方法接受一个唯一的属性, 返回一个 React 元素, 那它就是一个 React 组件。

  • 实例:最简单的组件实现

最简单的组件是函数组件, 如同我们在上面所说明的那样, 如果一个函数接受一个唯一的属性, 返回一个 React 元素;那么我们就可以认为它是一个标准的 React 函数组件, 这种也可以称之为无状态组件, 因为它没有自己的 constructor 方法, 无法定义自己的状态集 state;

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

也可以使用 ES 中所学习的类来定义, 下面这个实现与上面的实现在 React 中是等效的, 我们一般将这种使用类的方式定义的组件称之为类组件或者有状态组件, 它可以在自己内部的 constructor 中定义一个自身的状态集 state, 然后可以在组件中对自己进行一些改变;

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  • 什么是 State

React 中的 state 主要作用是组件用于保存、控制及修改组件自己的状态, 它只能在 constructor 中初始化, 我们可以用 state 来完成对行为的控制、数据的更新及界面的渲染, 由于组件不能修改父组件传入的 props, 所以我们需要使用 state 来存储组件自身需要的数据, 它的每次改变都会引发组件的更新。即每次数据的更新都是通过修改 state 属性的值, 然后 ReactJS 内部会监听 state 属性的变化, 一旦发生变化, 就会触发组件的 render 方法来更新 DOM 结构。
归纳起来一句话: state 是组件用来存储自身数据的一个对象, 它是可以改变的, 它的每次改变都会引发组件的更新。

  • 实例:State 的简单操作

class Welcome extends React.Component {
  // 构造函数
  constructor(props) {
    // 继承父类React.Component的属性和方法
    super(props);
    // 设定组件state对象
    this.state = {
      msg: 'Hello',
    };
  },
  // 事件执行方法
  onClick() {
    // 事件被触发后, 通过steState来改变state中的属性
    this.setState({
      msg: 'Hallo'
    })
  },
  render() {
    return <h1 onClick="onClick">${this.state.msg}, {this.props.name}</h1>;
  }
}

  • 哪些属性应该放到 state 中去

上面我们说到了, 我们可以用 state 来完成对行为的控制、数据的更新及界面的渲染, 所以为了避免不必要的函数调用或 Dom 渲染, 我们需要判断每一个变量是否需要记录成一个 state。

.1 变量如果是通过 props 从父组件中获取, 就不需要
.2 如果这个变量可以通过其他的 state 属性或者 props 属性经过数据处理得到, 就不需要
.3 如果变量在 render 中没有使用到, 就不需要
.4 变量在整个生命周期中都保持不变时, 也不需要

  • setState

在 hooks 出现之前, setState 是 React 中使用频率最高的一个 API, 因为 React 中并没有像 Vue 中那样去实现了一个 Object.defineProperty 来监听数据的变化, 所以, 我们想要在数据改变时能让 react 知道数据发生了变化并且重新渲染 view 层就必须使用 setState 方法来通知 React 数据发生了变化:

  1. 最常用的一种: 我们不需要进行计算, 也不需要实时获取改变后的内容, 直接 setState 就行
onClick() {
  this.setState({
    msg: 'Hallo'
  });
}

  1. 带回调函数的: setState 方法主要是告诉 React 组件有数据需要更新, 可能会导致重新渲染。所以, 为了避免每一次 setState 都重新渲染, React 在这里做了一个节流的封装, 在接收到一个 setState 操作后, 首先会将它放到一个队列内, 然后去检查是否正在更新组件, 如果正在更新组件, 无论你调用了多少次 setState, 它只会将你的操作放入这个队列, 等待当前的更新操作完成后再执行。
onClick() {
  this.setState({
    msg: 'Hallo'
  });
  // 节流封装会让上面的改变无法实时呈现
  console.log(this.state.msg)
  // 'Hello'
}

这样的话就有点小尴尬了, 我们总会有些基于最新的 state 来实现的业务流程, React 这样一搞可能就没办法进行了, 虽然官方推荐我们在 componentDidUpdate 中获取并实现业务, 但这样的话整个的代码逻辑就分离了, 可能会引起一些阅读的不便, 甚至造成一些代码冗余:

// 举个不太好的例子, 比如我们某次点击时需要对某个属性累加两次
onClick() {
  this.setState({
    num: this.state.num + 1
  });
  this.setState({
    num: this.state.num + 1
  });
}

所以, React 在这里给我们提供了一个方法可以实时获取被改变的属性: setState 方法还可以接受第二个参数用于接收一个回调函数, 当 setState 队列被执行完毕时, React 会执行这个回调函数, 这样的话我们就可以在这个回调函数中获取被改变的 state 属性的值了:

onClick() {
  this.setState({
    msg: 'Hallo'
  },
  // 这个是setState成功后的回调, 它在setState执行完成后组件开始渲染前被调用
  () => {
    console.log(this.state.msg)
  });
}

  1. 需要进行一些计算的: setState 的第一个参数不仅仅只是一个 state 对象, 它也可以是一个同步返回 state 对象的回调函数, 这个函数提供两个参数:参数一是当前的 state 对象, 参数二是当前的 props 对象, 我们可以在这个函数中对它们进行一些简单的计算后再返回; 注意这里不要使用 this.state, 因为我们刚才说了, setState 是一个异步的操作, 所以这里你使用 this.state 极有可能就会拿到一个在刚刚被改变的 state:
onClick() {
  this.setState(prevState => {
    // 我们可以在这里去做一些简单的计算
    return {
      msg: 'Hallo'
    }
  }, () => {
    // 这个是setState成功后的回调
    console.log(this.state.msg);
    // 'Hallo'
  });
}
  1. 刚刚上面说到了, setState 是一个异步的操作, 需要使用回调来重新读取被改变的值, 但也有例外的时候, 比如我们在一些类似于 setTimeout 这些异步方法中调用 setState 时, 因为 React 无法感知开发者的渲染顺序, 所以采用了直接更新 state 的操作, 而不会进行批量更新, 因为这种操作会导致 Dom 的立即渲染, 所以我们不建议使用这种方法。
componentDidMount() {
  this.setState({val: this.state.val + 1});
  console.log('第 1 次 log:', this.state.val);
  this.setState({val: this.state.val + 1});
  console.log('第 2 次 log:', this.state.val);

 setTimeout(() => {
  this.setState({val: this.state.val + 1});
  console.log('第 3 次 log:', this.state.val);
  this.setState({val: this.state.val + 1});
  console.log('第 4 次 log:', this.state.val);
 }, 0);
}
  • setState 实现的简单描述

在 React 的 setState 函数实现中, 会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到 enqueueSetState 队列中, isBatchingUpdates 默认值是 false, 也就表示 setState 会同步更新 this.state。

但是 setState 中有一个函数 batchedUpdates, 这个函数会把 isBatchingUpdates 修改为 true, 而当 React 在进行队列事件处理之前就会调用这个 batchedUpdates 函数, 造成的后果, 就是由 React 在接收到一个 setState 请求时, 不会直接更新 state。关于 isBatchingUpdates 方法,除了 enqueueSetState 队列更新时会调用 batchedUpdates 来标记当前更新状态, 所有的 React 生命周期函数在执行的时候也会修改 isBatchingUpdates 的值为 true。

最后有一种例外不得不提的是,当我们在一些类似于 setTimeout 这种异步函数中执行 setState 时, 因为 React 无法感知我们的渲染顺序, 所以它放弃了修改 isBatchingUpdates 而是直接更新了 state。

let oldState = { msg: '原始值' }
// 判断是否正在执行事件的标记
let isBatchingUpdates = false // isBatchingUpdates react通过这个判断是否正在进行更新state
// react.setState中这样存储数据队列
let enqueueSetState = [] // setState队列
// 这是我杜撰的..原码中叫什么忘了
let callbackList = [] // callback队列

// 这个方法在整个setState中很多地方在调用
function batchedUpdates() {
  isBatchingUpdates = !isBatchingUpdates;
}
// 假装这是个setState
function mySetState(state, callback) {
  // 首先将需要改写的state扔到一个叫enqueueSetState的队列里
  enqueueSetState.push(state)
  // 如果有回调函数把回调函数也扔到一个队列里
  callback && Object.prototype.toString.call(callback) === 'function' && callbackList.push(callback)
  // 判断是否正在更新,如果正在更新,等待更新完成后第45行的调用
  if (isBatchingUpdates) {
    return
  }
  // 修改状态表示正在更新state
  batchedUpdates()

  // 创建一个队列副本
  const newSetState = [...enqueueSetState]
  // 清空原有队列
  enqueueSetState = []
  // 上面的作法是为了在处理数据的同时不影响后续的操作排队

  // 写state的方法
  function setState(state) {
    console.log(Object.assign(oldState, state), '处理完毕,over!')
  }
  setTimeout(() => {
    // 如果没有在更新状态,开始处理数据
    const newState = newSetState.reduce((prev, next) => {
      return Object.assign(prev, next)
    }, {})
    // 数据合并完成,写入当前state
    setState(newState)
    // 数据写入完成,修改更新状态,表示可以进行另一个队列的操作了
    batchedUpdates()
    // 检查是不是还有数据在排队,如果有排队的,从头再来
    if (enqueueSetState.length) {
      mySetState()
    }
  })
}
mySetState({ msg: '哥是第一个,后来的等着' })
mySetState({ msg: '后来的排个队' })
mySetState({ msg: '排队ing...' })
setTimeout(() => {
  mySetState({ msg: '又没赶上。。继续排队ing...' })
  mySetState({ msg: 'me too...' })
}, 500)

小节结束

什么是组件,什么是 state,这两个问题,是需要大家死记的,不一定要用我讲的这些词语描述,但要大致能说清楚

setState 是一个很灵活的方法,它接受两个参数,参数一可以是一个 state 对象,也可以是一个实时返回 state 对象的函数,参数二是一个回调,用于实时获取改变后的 state 进行更多的业务处理

然后,setState 的简单描述也要记一下,最好是把这些英文单词发音都背下来,这样面试时一旦问起,你娴熟的术语描述将会大大提升你在面试官眼中的形象,关键是,这段描述代表着你已经把 setState 的这段源码看完且理解透彻了。

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

React 组件与状态 的相关文章

  • Eslint errorring 导入没有扩展名的 jsx

    我正在尝试在 es6 中导入 jsx 文件而不需要 jsx 扩展名 import LoginErrorDialog from LoginErrorDialogView Not import LoginErrorDialog from Log
  • Three.js:缩放几何图形后错误的 BoundingBox

    在我的场景中 我有一个简单的立方体 var test new THREE Mesh new THREE CubeGeometry 10 10 10 new THREE MeshBasicMaterial scene add test 该立方
  • IE从哪个版本开始支持Object.create(null)?

    您可以通过多种方式在 JavaScript 中创建对象 creates an object which makes the Object prototype of data var data1 new Object Object liter
  • 如何将内联 JavaScript 与 Express/Node.js 中动态生成的内容分开?

    对于具有几年 Web 开发经验但没有找到答案的人来说 这是一个有点菜鸟的问题程序员堆栈交换 or Google 我决定在这里问一下 我在用Express网络框架Node js 但这个问题并不特定于任何 Web 框架或编程语言 以下是从数据库
  • 摩卡 - Chai Karma“套件未定义”

    我对 jscript tdd 很陌生 遇到了问题 希望有人能告诉我我在做什么 在浏览器中运行测试 通过 HTML 文件 一切正常 通过节点和业力运行它们我得到以下异常 我想在 node js 主机的 karma 中使用 Mocha 和 Ch
  • 如何针对 Node.js 中发生的每个错误发送电子邮件?

    假设我的 node js 应用程序正在运行 如果出现错误 我的意思是所有错误 不仅仅是网络错误 如果出现错误 则很重要 我如何调用函数向我发送电子邮件 基本上 在我希望它写入 err out 之前 我希望向我发送一封电子邮件 我正在使用no
  • 可以在初始 DOM 解析期间/之前修改 DOM 吗?

    是否可以在初始 DOM 解析期间或之前修改 DOM 或者我是否必须等到 DOM 被解析和构建之后才能与其交互 更具体地说 是否有可能阻止 DOM 中的脚本元素使用用户脚本 内容脚本或 Chrome 或 Firefox 中的类似脚本运行 在解
  • 隐藏 Div 的父级

    我只是想隐藏父divcomments section div class content content green div div div 我试过这个 document getElementById comments section pa
  • 有没有办法使用 Rspec/Capybara/Selenium 将 javascript console.errors 打印到终端?

    当我运行 rspec 时 是否可以让 capybara selenium 向 rspec 报告任何 javascript console errors 和其他异常 我有一大堆测试失败 但当我手动测试它时 我的应用程序正在运行 如果不知道仅在
  • 防止 iOS 键盘在 cordova 3.5 中滚动页面

    我正在使用 Cordova 3 5 和 jQuery mobile 构建 iOS 应用程序 我在大部分应用程序中禁用了滚动功能 但是 当我选择输入字段时 iOS 键盘会打开并向上滚动页面 我不想要这个功能 由于输入足够高 键盘不会覆盖它 我
  • 使用 CSS 或 Javascript 填充动画

    我只是想知道是否可以使用 CSS 或 javascript 创建填充动画 基本上我想创建一个填充动画 如下图所示 http i40 tinypic com eit6ia png http i40 tinypic com eit6ia png
  • 刷新页面时保存用户的选择

    我目前有一个页面显示不同团队的数据 我有一些数据 用户可以单击使其处于 打开 或 关闭 状态 并为每个数据显示不同的图标 它基本上就像一个清单 只是没有物理复选框 我想记住哪些 复选框 已被选中 即使在用户刷新页面或关闭浏览器并稍后返回之后
  • 为什么我们在打字稿中使用 HTMLInputElement ?

    我们为什么使用 document getElementById ipv as HTMLInputElement value 代替 document getElementById ipv value 功能getElementById返回具有类
  • 有没有办法在 onclick 触发时禁用 iPad/iPhone 上的闪烁/闪烁?

    所以我有一个有 onclick 事件的区域 在常规浏览器上单击时 它不会显示任何视觉变化 但在 iPad iPhone 上单击时 它会闪烁 闪烁 有什么办法可以阻止它在 iPad iPhone 上执行此操作吗 这是一个与我正在做的类似的示例
  • 使用 Vue 的多模式组件

    我在 Vue 中实现动态模式组件时遇到问题 A common approach I follow to display a set of data fetched from the db is I dump each of the rows
  • 如何渲染变量(或 prop)内部的 jsx/html?

    const Footer gt let a b Hey b return div Some bold text a div 这只会呈现为Some bold text b Hey b 如何将粗体文本渲染为粗体 变量内容是我自己的 所以我不必担
  • Vue 和 Vuex:处理依赖的计算属性

    我的应用程序是一个使用 Vuex 在 Vue 中构建的精简电子表格 关键组件是TableCollection Table and Row The TableCollection有一个包含多个的数组Table对象 每个Table有一个包含多个
  • 在 JavaScript 循环之外声明变量可以提高速度和内存?

    C 也有类似的问题 但我们没有看到 JavaScript 的任何问题 在循环内声明变量是否可以接受 假设循环有 200 次迭代 使用样本 2 相对于样本 1 是否有性能要求 内存和速度 我们使用 jQuery 来循环 它提高了我们将 var
  • 带参数的事件监听器

    我想将参数传递给 JavaScript 中的事件侦听器 我已经找到了解决方案 但我无法理解它们为什么或如何工作以及为什么其他解决方案不起作用 我有 C C 背景 但是 Javascript 函数的执行有很大不同 您能否帮助我理解以下示例如何
  • 如何使用asm.js进行测试和开发?

    最近我读到asm js规范 看起来很酷 但是是否有任何环境 工具来开发和测试这个工具 这还只是处于规范阶段吗 您可以尝试使用 emscripten 和 ASM JS 1 并从侧分支在 firefox 构建中运行它 有关 asm js 的链接

随机推荐

  • 洛谷 1969 积木大赛——水题

    题目 https www luogu org problemnew show P1969 include
  • 常见的几种网络故障案例分析与解决

    故障1 交换机刚加电时网络无法通信 故障现象 交换机刚刚开启的时候无法连接至其他网络 需要等待一段时间才可以 另外 需要使用一段时间之后 访问其他计算机的速度才快 如果有一段时间不使用网络 再访问的时候速度又会慢下来 故障分析 由于这台交换
  • 完全背包问题求组合数和排列数

    518 零钱兑换 II 这个是完全背包问题的典型应用 由于只是求个数 不涉及到零钱排列情况不一样算两次的情况 所以两层for循环 外层遍历物品 内层遍历背包 class Solution public int change int amou
  • 阿里云播放器prismplayer抓包的一些理解

    Prismplayer是一套在线视频播放技术方案 同时支持Flash和Html5两种播放技术 可对播放器进行功能配置和皮肤定制 其在线使用文档地址为 入口 在甘肃交通视频云联网平台中用的就是该播放器 通过抓包发现 播放的是hls的ts流 下
  • mysql存储过程

    CREATE DEFINER root localhost PROCEDURE test BEGIN DECLARE i int DEFAULT 0 DECLARE classSize int DEFAULT 0 SELECT count
  • 给jupter设置新环境

    文章目录 给jupternotebook设置新环境 遇到的报错 添加路径的方法 给jupternotebook设置新环境 先在anaconda界面新建环境 conda env list 查看conda prompt下的有的环境变量 带星号的
  • Vue2使用插件合集

    Quill 插件描述 Vue 富文本编辑器 1 下载 vue quill editor npm i vue quill editor S 2 将vue quill editor引入到main js import VueQuillEditor
  • 任务调度器leetcode621

    问题 来自LeetCode 给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表 其中每个字母表示一种不同种类的任务 任务可以以任意顺序执行 并且每个任务都可以在 1 个单位时间内执行完 在任何一个单位时间 CPU 可以完成一
  • springcloudgateway集成hystrix

    目录 一 pom引入依赖 二 RestTemplate开启ribbon的负载均衡 LoadBalanced 三 yml配置和熔断降级的fallback接口 四 技术资料 springcloudgateway和Hystrix springcl
  • 如何设计高性能的分布式锁

    什么是分布式锁 在 JVM 中 在多线程并发的情况下 我们可以使用同步锁或 Lock 锁 保证在同一时间内 只能有一个线程修改共享变量或执行代码块 但现在我们的服务都是基于分布式集群来实现部署的 对于一些共享资源 在分布式环境下使用 Jav
  • Antd Table 可编辑表格

    antd Table 官方文档提及了 可编辑单元格 可编辑行 这里解决 可编辑表格 主要思路是将 antd Table 可编辑行 与 antd Form List 相结合 将Table视为 Form List 中循环的 Form Item
  • 如何使用Hexo搭建属于自己的博客

    Hexo安装步骤 Hexo官网 环境准备 Nodejs Git node v npm v 安装Hexo npm install hexo cli g cd到你需要创建博客的文件夹 hexo init blog cd blog npm ins
  • Ubuntu系统连接罗技K380键盘

    近日向学习LInux系统的使用 便把windows系统卸载装上了Ubuntu 下面是罗技K380连接Ubuntu 系统的方法 先打开K380键盘的蓝牙 我选择2 然后进入电脑的终端 输入如下命令 bluetoothctl devices 此
  • 超详细的tomcat的下载安装和配置教程

    tomcat运行的前提是安装并配置了JDK 若没有安装配置JDK 先去安装配置JDK 如下链接 JDK 1 8的下载安装和环境变量的配置 详细步骤 一 下载tomcat 1 进入tomcat的下载 tomcat下载官网 2 点击进入 点击
  • FastDFS在Docker集群安装

    一 简介 FastDFS是由国人余庆所开发 其项目地址 https github com happyfish100 FastDFS是一个轻量级的开源分布式文件系统 主要解决了大容量的文件存储和高并发访问的问题 文件存取时实现了负载均衡 Fa
  • 基于Matlab模拟宇宙射线μ

    作者简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 matlab项目合作可私信 个人主页 Matlab科研工作室 个人信条 格物致知 更多Matlab仿真内容点击 智能优化算法 神经网络预测 雷达通信 无线传感器 电力系统 信号
  • springboot+react实现前后端交互

    一 搭建springboot后台 1 创建一个Springboot项目 然后导入pom依赖 本着一切从简的原则 如果不连接数据库的话 有个spring boot starter web的依赖就够用了
  • webpack5 loader

    文章目录 作用 基本使用 内联loader loader执行顺序 loader pitch 中断 pitch pitch示例 自定义loader 查找loader loader方法参数 同步loader return方式 this call
  • mysql - 索引

    索引的分类 主键索引 唯一索引 普通索引 组合索引 以及全文索引 主键索引 非空唯一索引 一个表只有一个主键索引 在 innodb 中 主键索引的B 树包含表数据信息 PRIMARY KEY key 唯一索引 不可以出现相同的值 可以有NU
  • React 组件与状态

    企业项目实战 gt 第二部分 gt React 基础回顾 React 组件与 State 什么是组件 组件是什么 每个程序员都有自己的理解 在传统语言中 组件的定义一般来说是一个从特定的组件类中派生出来的特定的对象 而在早期的前端开发者眼里