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

相关推荐
小李子呢02111 小时前
前端八股Vue---Vue-router路由管理器
前端·javascript·vue.js
洛_尘3 小时前
Python 5:使用库
java·前端·python
Bigger3 小时前
Bun 能上生产吗?我的实战结论
前端·node.js·bun
kyriewen4 小时前
你的前端滤镜慢得像PPT?用Rust+WebAssembly,一秒处理4K图
前端·rust·webassembly
kyriewen114 小时前
你等的Babel编译,够喝三杯咖啡了——用Rust重写的SWC,只需眨个眼
开发语言·前端·javascript·后端·性能优化·rust·前端框架
IT_陈寒5 小时前
SpringBoot自动配置坑了我,原来要这样绕过去
前端·人工智能·后端
东方小月5 小时前
Claude Code 完整上手指南:MCP、Skills、第三方模型配置一次搞定
前端·人工智能·后端
XZ探长5 小时前
基于 Trae Solo 移动办公修复 Vue3 前端服务问题
前端
小程故事多_805 小时前
[大模型面试系列] 深度解析ReAct框架,大模型Agent的“思考+行动”底层逻辑
人工智能·react.js·面试·职场和发展·智能体
蝎子莱莱爱打怪5 小时前
Claude Code 省 Token 小妙招:RTK + Caveman 组合拳
前端·人工智能·后端