步入React正殿 - State进阶

news2025/1/21 12:09:49

目录

扩展学习资料

State进阶知识点

状态更新扩展

shouldComponentUpdate

PureComponent

为何使用不变数据【保证数据引用不会出错】

 单一数据源

 @/src/App.js

@/src/components/listItem.jsx

状态提升

 @/src/components/navbar.jsx

@/src/components/listPage.jsx

@src/App.js

有状态组件&无状态组件

Stateful【有状态】和Stateless【无状态】的区别

Stateful

Stateless

小结

练习


扩展学习资料

预习资料名称 

链接

备注

不可变数据

https://github.com/immutable-js/immutable-js

JS内存管理

内存管理 - JavaScript | MDN

状态提升

mangojuice.top - 该网站正在出售! - mangojuice 资源和信息。

扩展阅读

context管理状态

http://react.html.cn/docs/context.html  

聊一聊我对 React Context 的理解以及应用 - 掘金

扩展阅读

State进阶知识点

  • 通过条件判断优化渲染
  • 使用不可变数据
  • 状态提升
  • 使用无状态组件

状态更新扩展

阻止不必要的render方法执行

shouldComponentUpdate

// render渲染执行前调用的函数,返回false,可以有效的阻止不必要的render方法执行
  shouldComponentUpdate(nextProps, nextState) {
    console.log('props', this.props, nextProps);
    console.log('state', this.state, nextState);
    if(this.state.count === nextState.count) {
        return false
    }
    if(this.props.id === nextProps.id) {
        return false
    }
    return true
  }

PureComponent

import React, { PureComponent } from 'react';
class ListItem extends PureComponent {}

为何使用不变数据【保证数据引用不会出错】

// ...
handleDelete = (id) => {
    // 使用不可变数据, filter返回一个新数组
    const listData = this.state.listData.filter((item) => item.id !== id);
    this.setState({
      listData,
    });
  };
  handleAmount = () => {
    // 如不使用新的数组【没有使用不可变数据】, state变化,不会重新渲染UI
    //
    const _list = this.state.listData.concat([]);
    /* 
      pop() 方法用于删除数组的最后一个元素并返回删除的元素。
      注意:此方法改变数组的长度!
      提示: 移除数组第一个元素,请使用 shift() 方法。
    */
    _list.pop();
    this.setState({
      listData: _list,
    });
  };
// ...

如下图,如果没有创建新的引用,在PureComponent中,不会调用render

 如下图,使用不可变数据,可以避免引用带来的副作用,使得整个程序数据变的易于管理

 单一数据源

handleReset = () => {
    // 使用map方法创建一个新的数组
    const _list = this.state.listData.map((item) => {
      // ... 解构符
      const _item = { ...item };
      _item.value = 0;
      return _item;
    });
    this.setState({
      listData: _list,
    });
    // 此时props数据变化,子组件state.count没变化
    // 原因出在没有使用单一数据源
  };

 @/src/App.js

import React, { PureComponent } from 'react';
import ListItem from './components/listItem';
import ListItemFunc from './components/listItemFunc';
import style from './components/listitem.module.css';

// eslint-disable-next-line no-unused-vars
class App extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      listData: [
      {
        id: 1,
        name: 'sony 65寸高清电视',
        price: 4000,
        stock: 1,
        value: 4,
      },
      {
        id: 2,
        name: '华为 Meta30',
        price: 6000,
        stock: 12,
        value: 2,
      },
      {
        id: 3,
        name: '华硕 玩家国度笔记本',
        price: 10000,
        stock: 11,
        value: 1,
      }],
  	 };
  }
  renderList() {
    return this.state.listData.map((item) => {
      return (
        <ListItem
          key={item.id}
          data={item}
          onDelete={this.handleDelete}
          onDecrease={this.handleDecrease}
          onAdd={this.handleAdd}
        />
      );
    });
  }
  handleDelete = (id) => {
    // 使用不可变数据, filter返回一个新数组
    const listData = this.state.listData.filter((item) => item.id !== id);
    this.setState({
      listData,
    });
  };
  handleDecrease = (id) => {
    // 使用不可变数据, filter返回一个新数组
    const _data = this.state.listData.map((item) => {
      if (item.id === id) {
        const _item = { ...item };
        _item.value--;
        if (_item.value < 0) _item.value = 0;
        return _item;
      }
      return item;
    });
    this.setState({
      listData: _data,
    });
  };
  handleAdd = (id) => {
    // 使用不可变数据, filter返回一个新数组
    console.log(id);
    const _data = this.state.listData.map((item) => {
      if (item.id === id) {
        const _item = { ...item };
        _item.value++;
        return _item;
      }
      return item;
    });
    this.setState({
      listData: _data,
    });
  };
  handleAmount = () => {
    // 如不使用新的数组【没有使用不可变数据】, state变化,不会重新渲染UI
    //
    const _list = this.state.listData.concat([]);
    /* 
      pop() 方法用于删除数组的最后一个元素并返回删除的元素。
      注意:此方法改变数组的长度!
      提示: 移除数组第一个元素,请使用 shift() 方法。
    */
    _list.pop();
    this.setState({
      listData: _list,
    });
  };
  handleReset = () => {
    // 使用map方法创建一个新的数组
    const _list = this.state.listData.map((item) => {
      // ... 结构符
      const _item = { ...item };
      _item.value = 0;
      return _item;
    });
    this.setState({
      listData: _list,
    });
    // 此时props数据变化,子组件state.count没变化
    // 原因出在没有使用单一数据源
  };
  render() {
    return (
      <div className='container'>
        <button onClick={this.handleAmount} className='btn btn-primary'>
          减去最后一个
        </button>
        <button onClick={this.handleReset} className='btn btn-primary'>
          重置
        </button>
        {this.state.listData.length === 0 && (
          <div className='text-center'>购物车是空的</div>
        )}
        {this.renderList()}
      </div>
    );
  }
}

export default App;

@/src/components/listItem.jsx

// import React, { Component } from 'react';
import React, { PureComponent } from 'react';
import style from './listitem.module.css';
import classnames from 'classnames/bind';
const cls = classnames.bind(style);
class ListItem extends PureComponent {
  // 类的构造函数
  // eslint-disable-next-line no-useless-constructor
  constructor(props) {
    super(props);
  } 
  render() {
    console.log('item is rendering');
    return (
      <div className='row mb-3'>
        <div className='col-4 themed-grid-col'>
          <span style={{ fontSize: 22, color: '#710000' }}>
            {this.props.data.name}
          </span>
        </div>
        <div className='col-1 themed-grid-col'>
          <span className={cls('price-tag')}>¥{this.props.data.price}</span>
        </div>
        <div
          className={`col-2 themed-grid-col${
            this.props.data.value ? '' : '-s'
          }`}>
          <button
            onClick={() => {
              this.props.onDecrease(this.props.data.id);
            }}
            type='button'
            className='btn btn-primary'>
            -
          </button>
          <span className={cls('digital')}>{this.props.data.value}</span>
          <button
            onClick={() => {
              this.props.onAdd(this.props.data.id);
            }}
            type='button'
            className='btn btn-primary'>
            +
          </button>
        </div>
        <div className='col-2 themed-grid-col'>
          ¥ {this.props.data.price * this.props.data.value}
        </div>
        <div className='col-1 themed-grid-col'>
          <button
            onClick={() => {
              this.props.onDelete(this.props.data.id);
            }}
            type='button'
            className='btn btn-danger btn-sm'>
            删除
          </button>
        </div>
      </div>
    );
  }
}
export default ListItem;

状态提升

处理组件和子组件数据传递,自顶向下单向流动

 @/src/components/navbar.jsx

import React, { PureComponent } from 'react';
class Nav extends PureComponent {
  render() {
    return (
      <nav className='navbar navbar-expand-lg navbar-light bg-light'>
        <div className='container'>
          <div className='wrap'>
            <span className='title'>NAVBAR</span>
            <span className='badge badge-pill badge-primary ml-2 mr-2'>
              {this.props.itemNum}
            </span>
            <button
              onClick={this.props.onReset}
              className='btn btn-outline-success my-2 my-sm-0 fr'
              type='button'>
              Reset
            </button>
          </div>
        </div>
      </nav>
    );
  }
}
export default Nav;

@/src/components/listPage.jsx

import React, { PureComponent } from 'react';
import ListItem from './listItem.jsx';
// 商品列表渲染
class ListPage extends PureComponent {
  renderList() {
    return this.props.data.map((item) => {
      return (
        <ListItem
          key={item.id}
          data={item}
          onDelete={this.props.handleDelete}
          onDecrease={this.props.handleDecrease}
          onAdd={this.props.handleAdd}
        />
      );
    });
  }
  render() {
    return (
      <div className='container'>
        {this.props.data.length === 0 && (
          <div className='text-center'>购物车是空的</div>
        )}
        {this.renderList()}
      </div>
    );
  }
}
export default ListPage;

@src/App.js

import React, { PureComponent } from 'react';
import Nav from './components/navbar';
import ListPage from './components/listPage';
const listData = [
  {
    id: 1,
    name: 'sony 65寸高清电视',
    price: 4000,
    stock: 1,
    value: 4,
  },
  {
    id: 2,
    name: '华为 Meta30',
    price: 6000,
    stock: 12,
    value: 2,
  },
  {
    id: 3,
    name: '华硕 玩家国度笔记本',
    price: 10000,
    stock: 11,
    value: 1,
  },
];
// eslint-disable-next-line no-unused-vars
class App extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      listData: listData,
    };
  }
  handleDelete = (id) => {
    // 使用不可变数据, filter返回一个新数组
    const listData = this.state.listData.filter((item) => item.id !== id);
    this.setState({
      listData,
    });
  };
  handleDecrease = (id) => {
    // 使用不可变数据, filter返回一个新数组
    const _data = this.state.listData.map((item) => {
      if (item.id === id) {
        const _item = { ...item };
        _item.value--;
        if (_item.value < 0) _item.value = 0;
        return _item;
      }
      return item;
    });
    this.setState({
      listData: _data,
    });
  };
  handleAdd = (id) => {
    // 使用不可变数据, filter返回一个新数组
    console.log(id);
    const _data = this.state.listData.map((item) => {
      if (item.id === id) {
        const _item = { ...item };
        _item.value++;
        return _item;
      }
      return item;
    });
    this.setState({
      listData: _data,
    });
  };
  handleAmount = () => {
    // 如不使用新的数组【没有使用不可变数据】, state变化,不会重新渲染UI
    //
    const _list = this.state.listData.concat([]);
    /* 
      pop() 方法用于删除数组的最后一个元素并返回删除的元素。
      注意:此方法改变数组的长度!
      提示: 移除数组第一个元素,请使用 shift() 方法。
    */
    _list.pop();
    this.setState({
      listData: _list,
    });
  };
  handleReset = () => {
    // 使用map方法创建一个新的数组
    const _list = this.state.listData.map((item) => {
      // ... 结构符
      const _item = { ...item };
      _item.value = 0;
      return _item;
    });
    this.setState({
      listData: _list,
    });
    // 此时props数据变化,子组件state.count没变化
    // 原因出在没有使用单一数据源
  };
  render() {
    return (
      <>
        <Nav itemNum={this.state.listData.length} onReset={this.handleReset} />
        <ListPage
          data={this.state.listData}
          handleAdd={this.handleAdd}
          handleAmount={this.handleAmount}
          handleDecrease={this.handleDecrease}
          handleDelete={this.handleDelete}
          handleReset={this.handleReset}
        />
      </>
    );
  }
}
export default App;

有状态组件&无状态组件

Stateful【有状态】和Stateless【无状态】的区别

Stateful

  • 类组件
  • 有状态组件
  • 容器组件

Stateless

  • 函数组件
  • 无状态组件
  • 展示组件

尽可能通过状态提升原则,将需要的状态提取到父组件中,而其他的组件使用无状态组件编写【父组件有状态,子组件无状态】

无状态组件简单好维护,单一从上而下的数据流

小结

  • 优化渲染
  • 使用不可变数据
  • 单一数据源以及状态提升
  • 无状态组件写法

练习

【题目1】 用单一数据源原则和状态提升原则改造购物车工程

【题目2】 目前Header中显示的是商品种类数量,改造成商品的总数目

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/883766.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AIGC】 快速体验Stable Diffusion

快速体验Stable Diffusion 引言一、安装二、简单使用2.1 一句话文生图2.2 详细文生图 三、进阶使用 引言 stable Diffusion是一款高性能的AI绘画生成工具&#xff0c;相比之前的AI绘画工具&#xff0c;它生成的图像质量更高、运行速度更快&#xff0c;是AI图像生成领域的里程碑…

【0815作业】搭建select的TCP客户端、poll客户端、tftp文件上传

IO多路复用&#xff08;重点&#xff01;&#xff01;&#xff01;&#xff09; 进程中如果同时需要处理多路输入输出流&#xff0c;在使用单进程单线程的情况下&#xff0c;同时处理多个输入输出请求。在无法用多进程多线程&#xff0c;可以选择用IO多路复用&#xff1b;由于不…

新手开抖店多久可以出单?

​开抖店是一种越来越流行的创业方式&#xff0c;在社交媒体平台上开店销售各种商品&#xff0c;比如服装、配饰、美妆和家居用品等等。对于新手来说&#xff0c;他们可能会很关心自己开抖店能够多久出单。虽然这个问题没有一个固定的答案&#xff0c;但是以下是一些关键的运营…

立中转债上市价格预测

立中转债 基本信息 转债名称&#xff1a;立中转债&#xff0c;评级&#xff1a;AA-&#xff0c;发行规模&#xff1a;8.998亿元。 正股名称&#xff1a;立中集团&#xff0c;今日收盘价&#xff1a;23.46元&#xff0c;转股价格&#xff1a;23.57元。 当前转股价值 转债面值 /…

synchronized 底层是如何实现的 ?

目录 1. synchronized 底层是如何实现的 2. 监视器的执行流程 1. synchronized 底层是如何实现的 synchronized 底层是通过 JVM 内置的 Monitor 监视器实现的。 以下代码&#xff0c;查看它运行时的字节码文件&#xff1a; public class SynchronizedMonitorDemo {public …

MySQL 主从复制遇到 1590 报错

作者通过一个主从复制过程中 1590 的错误&#xff0c;说明了 MySQL 8.0 在创建用户授权过程中的注意事项。 作者&#xff1a;王祥 爱可生 DBA 团队成员&#xff0c;主要负责 MySQL 故障处理和性能优化。对技术执着&#xff0c;为客户负责。 本文来源&#xff1a;原创投稿 爱可生…

【图像分类】理论篇(4)图像增强opencv实现

随机旋转 随机旋转是一种图像增强技术&#xff0c;它通过将图像以随机角度进行旋转来增加数据的多样性&#xff0c;从而帮助改善模型的鲁棒性和泛化能力。这在训练深度学习模型时尤其有用&#xff0c;可以使模型更好地适应各种角度的输入。 原图像&#xff1a; 旋转后的图像&…

当Visual Studio遇到 “当前不会命中断点.还没有为该文档加载任何符号“的情况

1.配置项目调试路径&#xff1a; 2.问题解决方案&#xff1a; VS配置调试路径不是默认路径时&#xff0c;需要看生成的文件是否在配置路径内&#xff0c;如果不在的话&#xff0c;可能发生"当前不会命中断点.还没有为该文档加载任何符号"的情况&#xff1b; 右键项…

【贪心】CF1779 D

不会1700 Problem - 1779D - Codeforces 题意&#xff1a; 思路&#xff1a; 首先手推样例&#xff0c;可以发现一些零碎的性质&#xff1a; 然后考虑如何去计算贡献 难点在于&#xff0c;当一个区间的两端是区间max时&#xff0c;怎么去计算贡献 事实上&#xff0c;只需要…

input输入框自动填充后消除背景色

一般自动填充后会有一个突出的浅蓝色背景&#xff0c;一定也不好看&#xff0c;所以想把它去掉&#xff1a; 这个时候&#xff0c;就要用到浏览器的样式设置了&#xff1a; input:-webkit-autofill {background: transparent;transition: background-color 50000s ease-in-ou…

浅谈5G技术会给视频监控行业带来的一些变革情况

5G是第五代移动通信技术&#xff0c;能够提供更高的带宽和更快的传输速度&#xff0c;这将为视频技术的发展带来大量机会。随着5G技术的逐步普及与商用&#xff0c;人们将能够享受到更加流畅的高清视频体验&#xff0c;并且5G技术还拥有更低的延迟和更高的网络容量。这些优势不…

Android Shape 的使用

目录 什么是Shape? shape属性 子标签属性 corners &#xff08;圆角&#xff09; solid &#xff08;填充色&#xff09; gradient &#xff08;渐变&#xff09; stroke &#xff08;描边&#xff09; padding &#xff08;内边距&#xff09; size &#xff08;大小…

校园外卖小程序怎么做

校园外卖小程序是为满足校园内学生和教职员工的外卖需求而开发的一种应用程序。它涵盖了从用户端、商家端、骑手端、电脑管理员到小票打印、多商户入驻等多个方面的功能&#xff0c;以下将逐一介绍。 1. 用户端功能&#xff1a;校园外卖小程序为用户提供了便捷的订餐和外卖服务…

谦卦-六爻皆吉

前言&#xff1a;满招损&#xff0c;谦受益&#xff0c;谦卦在六十四卦是唯一的六爻皆吉的卦&#xff0c;今天学习谦卦的卦辞和爻辞。 卦辞 亨&#xff0c;君子有终。 序卦&#xff1a;有大者不可以盈&#xff0c;故受之以谦 篆曰&#xff1a;谦&#xff0c;亨&#xff0c;天…

解读注解@Value占位符替换过程

之前写过一篇关于介绍Spring占位符替换原理的博客&#xff0c;传送门 &#xff1a;Spring的占位符是怎么工作的 在这篇文章基础上&#xff0c;再介绍一下Value替换原理&#xff0c;两篇文章有一定的相关性。 继续以上一篇的工程为例&#xff0c;项目结构一样&#xff0c;这里就…

微波雷达感应模块XBG-M556

一、概括 XBG-M556是一款采用多普勒雷达技术,专门检测物体移动的微波感应模块。采用2.9G微波信号检测&#xff0c;该模块具有灵敏度高&#xff0c;可靠性强&#xff0c;感应角度大&#xff0c;工作电压宽等特点。高电平输出&#xff0c;可直接驱动外部 LED灯或负载。输入电压高…

Linux中执行一个Sheel脚本/系统重启后自动执行脚本

Linux中执行一个Sheel脚本 一&#xff1a;编写一个重启Java服务的.sh脚本 Windows中创建一个restart.sh文件 将一下脚本内容copy中restart.sh文件中 #!/bin/bashJAR_NAME"cloud.jar" LOG_FILE"restart.log"# 进入目录 cd /opt/server/cloudRecord/# 检查…

探索API接口的奥秘:解析与应用

什么是API接口&#xff1f;为什么它如此重要&#xff1f; 在现代技术和互联网时代&#xff0c;API接口是互联网服务之间实现数据传输和交流的关键链接。 API&#xff08;应用程序编程接口&#xff09;是一组定义了不同软件组件之间交互的规则和约定。 它允许不同的软件系统之间…

通过Git使用GitHub

目录 一、建立个人仓库 二、配置SSH密钥 三、克隆仓库代码 四、推送代码到个人仓库 五、代码拉取 一、建立个人仓库 1.建立GitHub个人仓库&#xff0c;首先注册GitHub用户。注册好了之后&#xff0c;打开用户的界面 然后就是配置问题 配置好后拉到最下方点击create repos…

day9 STM32 I2C总线通信

I2C总线简介 I2C总线介绍 I2C&#xff08;Inter-Integrated Circuit&#xff09;总线&#xff08;也称IIC或I2C&#xff09;是由PHILIPS公司开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备&#xff0c;是微电子通信控制领域广泛采用的一种总线标准。 它是同步通…