React的基础API介绍(二)

news2024/11/16 7:32:01

目录

    • useState
      • useState 的基本原理
        • 1. 状态在函数组件中的引入
        • 2. useState 的工作机制
        • 3. Hook 状态与组件渲染
      • useState 的使用方法
        • 1. 基本用法
        • 2. 多个状态变量
        • 3. 更新状态
      • 注意事项与最佳实践
        • 1. 状态更新可能是异步的
        • 2. 不要直接修改状态
        • 3. 更新对象或数组状态
        • 4. 避免闭包陷阱
      • 进阶用法
        • 1. 自定义 Hook
        • 2. 状态与副作用的结合
      • useState 与 useReducer 的对比
      • 为什么 Hook 应该始终在组件顶层调用,不能在条件、循环或嵌套函数中使用?
        • 1. React 如何跟踪 Hooks
        • 2. 为什么需要保持 Hooks 的调用顺序一致
        • 3. 示例解释
        • 4. 如何在条件情况下使用 Hooks

useState

useState 是 React Hooks 中最基本也是最常用的一个 Hook,用于在函数组件中添加状态管理功能。它使得函数组件能够像类组件一样拥有内部状态,而无需编写类组件的样板代码。

useState 的基本原理

1. 状态在函数组件中的引入

在传统的 React 类组件中,状态(state)是通过 this.state 和 this.setState 来管理的。函数组件最初是无状态的,仅根据传入的 props 进行渲染。

useState 的引入改变了这一点,它允许在函数组件中引入状态。每次调用useState,都创建了一个状态变量和更新该状态的函数。

2. useState 的工作机制
  • 状态的持久化:useState 通过闭包的方式,在组件的多次渲染之间保持状态的持久化。
  • 状态更新:当调用状态更新函数时,React 会将新的状态值入队,并触发组件重新渲染。在下一次渲染时,useState 会返回更新后的状态值。
  • Hook 的调用顺序:React 依赖于 Hook 调用的顺序来正确地管理状态。因此,Hook 应该始终在组件顶层调用,不能在条件、循环或嵌套函数中使用。
3. Hook 状态与组件渲染
  • 组件重新渲染:当状态更新时,组件会重新渲染。函数组件会重新执行,生成新的 JSX。
  • 状态的稳定性:虽然组件函数会重新执行,但 useState 返回的状态在多次渲染之间是稳定的。React 内部通过一个链表结构来跟踪每个 Hook 的状态。

useState 的使用方法

1. 基本用法
import React, { useState } from 'react';

function Counter() {
  // 声明一个新的状态变量 "count",初始值为 0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>当前计数:{count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

export default Counter;

解释:

初始化状态:useState(0) 初始化了一个状态变量 count,初始值为 0。
状态变量:count 是当前的状态值。
更新函数:setCount 是更新状态的函数,调用它可以更新 count 的值并触发组件重新渲染。

2. 多个状态变量

可以在同一个组件中多次使用 useState,每个状态变量都是独立的。

function UserProfile() {
  const [name, setName] = useState('李四');
  const [age, setAge] = useState(30);

  return (
    <div>
      <p>姓名:{name}</p>
      <p>年龄:{age}</p>
      <button onClick={() => setAge(age + 1)}>增加年龄</button>
    </div>
  );
}
3. 更新状态

直接赋值更新:

setCount(newValue);

基于前一个状态更新(函数式更新):
当新的状态需要依赖之前的状态时,使用函数式更新。

setCount(prevCount => prevCount + 1);

好处: 避免了状态更新的异步性带来的问题,确保基于最新的状态进行计算。

注意事项与最佳实践

1. 状态更新可能是异步的

不可立即获取更新后的状态: 调用 setState 后,状态并不会立即更新,新的状态会在下一次渲染时生效。

避免依赖立即更新的状态:

// 错误示例
setCount(count + 1);
console.log(count); // 仍然是旧的 count 值

// 正确示例
setCount(prevCount => {
  const newCount = prevCount + 1;
  console.log(newCount); // 新的 count 值
  return newCount;
});
2. 不要直接修改状态

状态应该被视为不可变的。不要直接修改状态变量,而是创建新的状态值。

// 错误示例
state.value = newValue;

// 正确示例
setState({ ...state, value: newValue });
3. 更新对象或数组状态

useState 不会自动合并更新对象或数组,需要手动合并。

const [user, setUser] = useState({ name: '张三', age: 25 });

// 更新年龄
setUser(prevUser => ({ ...prevUser, age: prevUser.age + 1 }));
4. 避免闭包陷阱

在异步操作中,可能会捕获到旧的状态值。使用函数式更新可以避免这个问题。

useEffect(() => {
  const timer = setTimeout(() => {
    // 使用函数式更新,确保获取最新的状态值
    setCount(prevCount => prevCount + 1);
  }, 1000);

  return () => clearTimeout(timer);
}, []);

进阶用法

1. 自定义 Hook

可以将状态逻辑封装到自定义 Hook 中,以便在多个组件之间复用。

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);

  const increment = () => setCount(prev => prev + 1);
  const decrement = () => setCount(prev => prev - 1);

  return { count, increment, decrement };
}

// 在组件中使用
function Counter() {
  const { count, increment, decrement } = useCounter(10);

  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={increment}>增加</button>
      <button onClick={decrement}>减少</button>
    </div>
  );
}
2. 状态与副作用的结合

useState 通常与 useEffect 一起使用,响应状态的变化执行副作用操作。

function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    let isMounted = true;

    fetchData().then(response => {
      if (isMounted) setData(response);
    });

    return () => {
      isMounted = false;
    };
  }, []);

  return <div>{data ? data.content : '加载中...'}</div>;
}

useState 与 useReducer 的对比

当状态逻辑较为复杂,或者状态更新依赖于前一个状态时,useReducer 可能是更好的选择。

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'decrement':
      return { ...state, count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>计数:{state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>增加</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>减少</button>
    </div>
  );
}

useReducer 的优势:

更清晰的状态管理:适用于复杂状态逻辑,状态更新逻辑集中在 reducer 函数中。

为什么 Hook 应该始终在组件顶层调用,不能在条件、循环或嵌套函数中使用?

1. React 如何跟踪 Hooks
  • Hooks 的调用顺序:React 通过组件中 Hooks 的调用顺序来跟踪每个 Hook 对应的状态。这意味着每次渲染时,Hooks 必须按照相同的顺序被调用。

  • Hook 链表:在内部,React 维护了一个 Hook 链表,记录了每个 Hook 在组件中的位置。当组件重新渲染时,React 依赖于这个调用顺序来正确地匹配当前的 Hook 与之前的状态。

2. 为什么需要保持 Hooks 的调用顺序一致
  • 状态与 Hook 位置关联:由于状态是与 Hook 调用的位置(即调用顺序)相关联的,如果 Hooks 的调用顺序发生变化,React 就无法正确地为每个 Hook 提供对应的状态。

  • 避免状态错位:如果在条件、循环或嵌套函数中调用 Hooks,可能会导致 Hooks 的调用数量或顺序在不同的渲染中发生变化,导致状态错位。

3. 示例解释
  • 错误示例:在条件语句中使用 Hook
function MyComponent({ show }) {
  if (show) {
    useState(0);
  }
  // 其他代码
}

问题:

当 show 为 true 时,useState 被调用。
当 show 为 false 时,useState 未被调用。
这导致在不同的渲染中,Hooks 的调用数量和顺序发生了变化。

后果:

React 无法正确地匹配 Hook 与之前的状态。
可能会导致状态错位、错误的状态值,甚至引发难以调试的错误。

正确的做法

function MyComponent({ show }) {
  const [state, setState] = useState(0);

  // 根据条件渲染内容
  if (show) {
    // 使用 state
  }

  // 其他代码
}

始终在顶层调用 useState,确保每次渲染时 Hooks 的调用顺序一致。

  • 错误示例:在循环中使用 Hook
function MyComponent({ items }) {
  items.forEach(item => {
    useEffect(() => {
      // 对每个 item 进行副作用操作
    }, [item]);
  });

  // 其他代码
}

问题:

items 的数量可能会变化,导致 useEffect 的调用次数不一致。
Hooks 的调用顺序和数量在不同的渲染中不一致。

后果
React 无法正确地管理 Hooks,导致状态错位。

正确的做法

function MyComponent({ items }) {
  // 使用一个 Hook 处理所有 items
  useEffect(() => {
    items.forEach(item => {
      // 对每个 item 进行副作用操作
    });
  }, [items]);

  // 其他代码
}

将 Hooks 调用放在顶层,不受循环或条件的影响。

4. 如何在条件情况下使用 Hooks

虽然不能在条件语句中调用 Hooks,但可以通过在 Hooks 内部处理条件逻辑来实现需求。

示例:

function MyComponent({ show }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    if (show) {
      // 执行副作用
      fetchData().then(result => setData(result));
    }
  }, [show]);

  // 其他代码
}

解释:useEffect 始终被调用,但在内部根据 show 的值决定是否执行副作用操作。

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

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

相关文章

HarmonyOs DevEco Studio小技巧31--画布组件Canvas

那天我们用画布实现了文字颜色的渐变&#xff0c;实际上画布还有很多好玩的功能&#xff0c;接下来让我们一起试一下画布怎么玩 Canvas 提供画布组件&#xff0c;用于自定义绘制图形。 接口 Canvas Canvas(context?: CanvasRenderingContext2D | DrawingRenderingContext…

C语言.冒泡排序的练习

题目&#xff1a; 1.用C语言编程&#xff0c;用冒泡排序将一个元素个数为20&#xff0c;的一维数组&#xff0c;由大到小的降序。 分析&#xff1a;1.首先创建一个数组&#xff0c;元素个数为20. 2.其次是按降序排列。 3.最后用冒泡排序。 运行代码&#xff1a; #include&l…

RT-DETR融合GhostModel V3及相关改进思路

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《GhostNetV3: Exploring the Training Strategies for Compact Models》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/pdf/2404.11202v1 代码链接&#xff1a;https:…

基于yolov8、yolov5的鸟类分类系统(含UI界面、训练好的模型、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型。模型十分重要&#xff0c;因为有些同学的电脑没有 GPU&#xff0…

如何保证MySQL与Redis缓存的数据一致性?

文章目录 一、引言二、场景来源三、高并发解决方案1. 先更新缓存&#xff0c;再更新数据库2. 先更新数据库&#xff0c;再更新缓存3. 先删除缓存&#xff0c;再更新数据库4. 先更新数据库&#xff0c;再删除缓存小结 四、拓展方案1. 分布式锁与分布式事务2. 消息队列3. 监听bin…

Chromium 中sqlite数据库操作演示c++

本文主要演示sqlite数据库 增删改查创建数据库以及数据库表的基本操作&#xff0c;仅供学习参考。 一、sqlite数据库操作类封装&#xff1a; sql\database.h sql\database.cc // Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-sty…

pycharm分支提交操作

一、Pycharm拉取Git远程仓库代码 1、点击VCS > Get from Version Control 2、输入git的url&#xff0c;选择自己的项目路径 3、点击Clone&#xff0c;就拉取成功了 默认签出分支为main 选择develop签出即可进行开发工作 二、创建分支&#xff08;非必要可以不使用&#xf…

河道无人机雷达测流监测系统由哪几部分组成?

在现代水利管理中&#xff0c;河道无人机雷达监测系统正逐渐成为一种重要的工具&#xff0c;为河道的安全和管理提供了强大的技术支持。那么&#xff0c;这个先进的监测系统究竟由哪几部分组成呢&#xff1f; 河道无人机雷达监测系统工作原理 雷达传感器通过发射电磁波或激光束…

使用pycrawlers下载huggingface报错list index out of range问题解决

使用pycrawlers下载huggingface失败 错误&#xff1a;list index out of range 问题描述&#xff1a;当批量下载整个huggingface仓库的时候会报错&#xff0c;并且没有报错提示 分析&#xff1a;应该是哪个链接请求结果不存在数据 结果&#xff1a;当下载仓库存在文件夹下面只…

清华大学提出基于ESKF的松耦合里程计RINO:一种具有非迭代估计的精确、稳健的雷达惯性里程计

Abstract 精确的定位和建图对于实现自动驾驶车辆的自主导航至关重要。然而&#xff0c;当GNSS信号失效或在极端天气条件&#xff08;例如雾、雨和雪&#xff09;下&#xff0c;车体运动估计仍然面临重大挑战。近年来&#xff0c;扫描雷达因其较强的穿透能力成为一种有效的解决…

微信小程序之路由跳转传数据及接收

跳转并传id或者对象 1.home/index.wxml <!--点击goto方法 将spu_id传过去--> <view class"item" bind:tap"goto" data-id"{{item.spu_id}}"> 结果: 2.home/index.js goto(event){// 路由跳转页面,并把id传传过去//获取商品idlet i…

tensorflow案例6--基于VGG16的猫狗识别(准确率99.8%+),以及tqdm、train_on_batch的简介

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 本次还是学习API和如何搭建神经网络为主&#xff0c;这一次用VGG16去对猫狗分类&#xff0c;效果还是很好的&#xff0c;达到了99.8% 文章目录 1、tqdm…

海康大华宇视视频平台EasyCVR私有化视频平台服务器选购主要参数有哪些?

在构建现代服务器和视频监控系统时&#xff0c;选择合适的硬件配置和关键技术是确保系统性能和稳定性的基础。服务器选购涉及到多个关键参数&#xff0c;这些参数直接影响到服务器的处理能力、数据存储、网络通信等多个方面。 同时&#xff0c;随着视频监控技术的发展&#xf…

Redisson的可重入锁

初始状态&#xff1a; 表示系统或资源在没有线程持有锁的情况下的状态&#xff0c;任何线程都可以尝试获取锁。 线程 1 获得锁&#xff1a; 线程 1 首次获取了锁并进入受保护的代码区域。 线程 1 再次请求锁&#xff1a; 在持有锁的情况下&#xff0c;线程 1 再次请求锁&a…

Java-01 深入浅出 MyBatis - MyBatis 概念 ORM映射关系 常见ORM 详细发展历史

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

C语言第13节:指针(3)

1. 回调函数 回调函数的基本思想是&#xff0c;将函数指针作为参数传递给另一个函数&#xff0c;并在需要时通过这个函数指针调用对应的函数。这种方式允许一个函数对执行的内容进行控制&#xff0c;而不需要知道具体的实现细节。 回调函数在以下场景中尤为有用&#xff1a; …

Tensorflow基本概念

简介&#xff1a;本文从Graph讲到Session&#xff0c;同时讲解了tf.constant创建tensor的用法和variable需要初始化的知识点&#xff0c;可以给你打好一个学习Tensorflow的基础。本文都是基于TensorFlow1.14.0的版本下运行。 本专栏将会系统的讲解TensorFlow在1.14.0版本下的各…

【包教包会】CocosCreator3.x框架——带翻页特效的场景切换

一、效果演示 二、如何获取 1、https://gitee.com/szrpf/TurnPage 2 2、解压&#xff0c;导入cocos creator&#xff08;版本3.8.2&#xff09;&#xff0c;可以直接运行Demo演示 三、算法思路 1、单场景 页面预制体 通过loadScene来切换页面&#xff0c;无法实现页面特效…

【MySQL 保姆级教学】事务的自动提交和手动提交(重点)--上(13)

目录 1. 什么是事务&#xff1f;2. 事务的版本支持3. 事务提交的方式3.1 事务提交方式的分类3.2 演示的准备的工作3.2.1 创建表3.2.2 MySQL的服务端和客户端3.2.3 调低事务的隔离级别 4. 手动提交4.1 手动提交的命令说明4.2 示例一4.3 示例二4.4 示例三4.5 示例四 5. 自动提交5…

几何合理的分片段感知的3D分子生成 FragGen - 评测

FragGen 来源于 2024 年 3 月 25 日 预印本的文章&#xff0c;文章题目是 Deep Geometry Handling and Fragment-wise Molecular 3D Graph Generation&#xff0c; 作者是 Odin Zhang&#xff0c;侯廷军&#xff0c;浙江大学药学院。FragGen 是一个基于分子片段的 3D 分子生成模…