React 基础 - useState、useContext/createContext

前言

本文新手向解释了 react useState 的更新逻辑和基本使用方法,需要了解真实且具体的实现原理,请移步Fiber。

useState

ts 复制代码
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];

基本Demo

tsx 复制代码
function FunctionComponent(props: any) {
  const [count1, setCount1] = useState()
  const [count2, setCount2] = useState(() => props.count || 0)
  
  useEffect(() => {
      setCount1(prevState => prevState + 1)
  }, [])
  
  return <div>{count1}</div>
}

没什么好说的,基本用法就这些。注意点:

  • 基本使用规范:仅可在组件或hook顶层调用useState来声明一个变量。不可在循环或条件语句中调用,这里不做过多说明。

  • set函数仅会影响下一次渲染中state返回的内容,

  • 多次执行set仅会返回最后一次执行结果。

  • 其它要点请仔细看下面示例,以时间线的形式描述了state更新时机、浏览器更新时机及副作用执行时机,同时解释了小白常见两个问题:

    • state更新缺无法获取最新值;
    • state更新死循环:
    tsx 复制代码
    function FunctionComponent() {
      const [count, setCount] = useState(0)
    
      const handleClick1 = () => {
        setCount(count + 1)
        setCount(count + 1)
        console.log('handleClick1: ', count)
        setCount(count + 1)
      }
      const handleClick2 = () => {
        setCount(count => count + 1)
        setCount(count => { 
          console.log('setCount: ', count)
          return count + 1
        })
        console.log('handleClick2: ', count)
        setCount(count => count + 1)
      }
    
      useEffect(() => { 
        setCount(1)
        console.log('effect1', count)
      }, [])
    
      useEffect(() => console.log('effect2', count), [count])
    
      console.log('function body: ', count)
    
      return <div>
        <button type='button' onClick={handleClick1}>button1: {count}</button>
        <button type='button' onClick={handleClick2}>button2: {count}</button>
      </div>
    }
    • 注:Fiber真实逻辑与下文有差异,这里仅为更方便理解。
    • 初始化执行函数
      • 第一次执行函数
      • 初始化count,useState 返回状态值0
      • 定义handleClick1,handleClick2
      • 缓存effect1,缓存effect2
      • function body:打印状态值为0
      • 浏览器渲染 count 状态值为 0
      • 执行effect1回调,记录 count 更新值 为1
      • effect1 打印当前状态值为0
      • 执行effect2回调
      • effect2 打印当前状态值为0
      • 副作用执行完毕,更新状态值为记录值:1
      • 第二次执行函数
      • useState 返回状态值1
      • 定义handleClick1,handleClick2
      • effect1无动作,effect2依赖更新,缓存effect2
      • function body:打印状态值为1
      • 浏览器渲染 count 状态值为 1
      • 执行effect2回调
      • effect2 打印当前状态值为1
      • 副作用执行完毕,无需更新渲染层。
    • 点击 button1
      • 执行handleClick1,当前状态值为1
        • 记录 count 更新新值 为1 + 1
        • 记录 count 更新新值 为1 + 1
        • handleClick1 打印当前状态值为1
        • 记录 count 更新新值 为1 + 1
      • 顺序更新三次记录值
        • count 记录值更新值为2
        • count 记录值更新值为2
        • count 记录值更新值为2
      • 更新状态值为记录值:2
      • 第三次执行函数
        • useState 返回状态值2
        • 定义handleClick1,handleClick2
        • effect1无动作,effect2依赖更新,缓存effect2
        • function body:打印状态值为2
        • 浏览器渲染 count 状态值为 2
        • 执行effect2回调
        • effect2 打印当前状态值为2
        • 副作用执行完毕,无需更新渲染层。
    • 点击 button2
      • 执行handleClick2,当前状态值为2
        • 记录 count 更新函数:count => count + 1
        • 记录 count 更新函数:count => { console.log('setCount: ', count); return count + 1 }
        • handleClick2 打印当前状态值为2
        • 记录 count 更新函数:count => count + 1
      • 顺序执行三个更新函数,将记录值作为入参,将返回值作为新记录值
        • 第一次:当前记录值为2,count 记录值更新值为2 + 1
        • 第二次:setCount 打印当前记录值为3,count 记录值更新值为3 + 1
        • 第一次:当前记录值为4, count 记录值更新值为4 + 1
      • 更新状态值为记录值:5
      • 第四次执行函数
        • useState 返回状态值5
        • 定义handleClick1,handleClick2
        • effect1无动作,effect2依赖更新,缓存effect2
        • function body:打印状态值为5
        • 浏览器渲染 count 状态值为 5
        • 执行effect2回调
        • effect2 打印当前状态值为5
        • 副作用执行完毕,无需更新渲染层。

useContext

ts 复制代码
function createContext<T>(defaultValue: T): Context<T>;
function useContext<T>(context: Context<T>): T;

基本DEMO

tsx 复制代码
const Context = createContext()

function Demo() { 
  return (
    <Context.Provider value={sameValue}>
      <Child/>
    </CountContext2.Provider>
  )
}

function Child() {
  const value = useContext(Context)
  return <div>{value}</div>
}

没什么好说的,基本用法就这些。存值取值。后续更新状态管理会涉及到 useContextuseState的碰撞结果。

相关推荐
Up九五小庞15 小时前
开源埋点分析平台 ClkLog 本地部署 + Web JS 埋点测试实战--九五小庞
前端·javascript·开源
摘星编程16 小时前
React Native + OpenHarmony:UniversalLink通用链接
javascript·react native·react.js
qq_1777673716 小时前
React Native鸿蒙跨平台数据使用监控应用技术,通过setInterval每5秒更新一次数据使用情况和套餐使用情况,模拟了真实应用中的数据监控场景
开发语言·前端·javascript·react native·react.js·ecmascript·harmonyos
烬头882116 小时前
React Native鸿蒙跨平台应用实现了onCategoryPress等核心函数,用于处理用户交互和状态更新,通过计算已支出和剩余预算
前端·javascript·react native·react.js·ecmascript·交互·harmonyos
2601_9495936518 小时前
基础入门 React Native 鸿蒙跨平台开发:卡片组件
react native·react.js·harmonyos
天人合一peng18 小时前
Unity中button 和toggle监听事件函数有无参数
前端·unity·游戏引擎
方也_arkling19 小时前
别名路径联想提示。@/统一文件路径的配置
前端·javascript
毕设源码-朱学姐19 小时前
【开题答辩全过程】以 基于web教师继续教育系统的设计与实现为例,包含答辩的问题和答案
前端
qq_1777673719 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos
qq_1777673719 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体