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>
}

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

相关推荐
橙光成长39 分钟前
PDF离线工具箱深度解析:4MB纯前端单文件,40+功能全本地运行,文件绝不上传
前端·pdf·1024程序员节
小poop7 小时前
CSS 基础与实战:从入门到精通网页美化
前端
Csvn9 小时前
运行时性能
前端
Never_every999 小时前
位图转矢量工具哪个好?实测抠抠图AI位图转矢量工具
大数据·前端
paopaokaka_luck10 小时前
宠物门店经营管理系统(销售与库存联动、进货自动记账、 退换与退款关联、员工个人业绩、ECharts 经营分析、滞销识别与客户分层、宠物档案年龄计算)
前端·javascript·spring boot·mysql·echarts·mybatis·1024程序员节
空中湖10 小时前
用 React 写数据大屏:为什么我不建议用图表库
前端·react.js·前端框架
欣欣之王来了12 小时前
原始HTML插值:v-html指令的使用与安全风险
前端·学习·架构·项目·vue3教程
IT_陈寒12 小时前
Redis的LRU淘汰策略坑了我一整天
前端·人工智能·后端
计算机魔术师12 小时前
砍半价格、参数再涨,国产旗舰追到 Opus 鼻尖了
前端
特立独行的猫A12 小时前
10 MB 的桌面 AI 客户端:用仓颉 + 系统 WebView 替代 Electron
前端·harmonyos