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的碰撞结果。

相关推荐
C_心欲无痕13 小时前
ts - tsconfig.json配置讲解
linux·前端·ubuntu·typescript·json
清沫13 小时前
Claude Skills:Agent 能力扩展的新范式
前端·ai编程
yinuo13 小时前
前端跨页面通信终极指南:方案拆解、对比分析
前端
yinuo14 小时前
前端跨页面通讯终极指南⑨:IndexedDB 用法全解析
前端
xkxnq14 小时前
第二阶段:Vue 组件化开发(第 16天)
前端·javascript·vue.js
烛阴14 小时前
拒绝配置地狱!5 分钟搭建 Three.js + Parcel 完美开发环境
前端·webgl·three.js
xkxnq15 小时前
第一阶段:Vue 基础入门(第 15天)
前端·javascript·vue.js
anyup16 小时前
2026第一站:分享我在高德大赛现场学到的技术、产品与心得
前端·架构·harmonyos
BBBBBAAAAAi16 小时前
Claude Code安装记录
开发语言·前端·javascript
xiaolyuh12316 小时前
【XXL-JOB】 GLUE模式 底层实现原理
java·开发语言·前端·python·xxl-job