React学习笔记 - useState解析

引入

对于函数组件

const App = props => {
    const [n, setN] = useState(0)
    //...
}

setN执行后:

  • n不会被setN改变(重要!)
  • 会触发UI更新,App()再次执行,再次useState(0)时,得到n的值不同

分析:

  • setNn的新值存入一个中间变量state
  • useStatestate读取n的最新值

模拟实现React.useState

v 1.0

用一个全局变量_state保存n的值

let _state
const useState2 = (initialValue)=>{
  _state = _state === undefined? initialValue: _state //判断_state是否存在
  const setState = (newValue)=>{
    _state = newValue
    render()
  }
  return [_state, setState]
}

const render = ()=>{ //模拟实现重渲染,源码并非如此
    ReactDOM.render(<App />, rootElement)
}

这样在每次调用useState2(0)的时候,都能使n得到最新的值。

问题:只能设置一个数据,有多个数据怎么办?

v 2.0

改进思路:将_state做成数组,按顺序保存数据

let _state = []
let index = 0
const useState2 = (initialValue)=>{
	const currentIndex = index
    const setState = (newValue)=>{
        _state[currentIndex] = newValue
        render()
    }
    index++
    return [_state[currentIndex], setState]
}

const render = ()=>{
    index=0 //每次渲染前重置index
    ReactDOM.render(<App />, rootElement)
}

【数组方案的缺陷】

每次运行App()时,设置数据的顺序必须完全一样,因此React规定useState不能用在if语句中

【目前的问题】

App组件用了_stateindex,其他组件怎么办?

【解决方法】

_stateindex放在组件对应的虚拟DOM上(略)

总结

  • 每个函数组件对应一个 React 节点,上面有stateindex

  • useState按其被调用顺序读取state[index]

  • setState会修改state,并触发更新

问题:每次重渲染会产生新的n,怎样才能做到每次渲染都是同一个n

如何每次使用相同的n

(重要!)在 React 中,不推荐这样做,可以改用 Vue 3

但 React 通过某些方法也能做到,可用useRef或者useContext(存疑)

useRef

useRef常用于对元素进行引用,但也可以用于此场景,用它声明一个数据nRef

const nRef = useRef(0) //它是一个对象:{current: 0}

useRef使得在每次重渲染时,nRef均为同一个对象,指向的地址固定。

但问题在于,对nRef进行修改不会触发重渲染,可以再引入一个setState函数,用它强制更新

const update = useState(null)[1] //调用update,传入新的值,会触发更新

【完整示例】

const App = props => {
    const nRef = React.useRef(0)
    const update = React.useState(null)[1]
    return (
    	<div>
        	{nRef.current}
            <button onClick={()=>{
                    nRef.current++
                    update(nRef.current)
                }}>+1</button>
        </div>
    )
}

useContext

context 的特点是可以跨层级传递数据给后代组件。

首先创建一个context

const myContext = React.createContext(null)

然后,在<myContext.Provider></myContext.Provider>包围的任何子组件中,都可以使用这个context

const App = props => {
  return (
    <myContext.Provider value={/* 传入数据 */}>
        <Child />
    </myContext.Provider>
  )
}

const Child = props => {
  const a = React.useContext(myContext) //a即为以上由value传入的数据
  return (
    <div>hi</div>
  )
}

作者:茶叶淡
链接:https://juejin.cn/post/6890086834610077704
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @   brave-sailor  阅读(588)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
历史上的今天:
2017-11-24 iOS方法重写
2016-11-24 Android 开发中 iBeacon的使用
2016-11-24 Android与IOS的UUID的区别
2016-11-24 android蓝牙4.0(BLE)开发之ibeacon初步
2016-11-24 Android 开发中 iBeacon的使用
2016-11-24 android setCompoundDrawables和setCompoundDrawablesWithIntrinsicBounds区别
2015-11-24 Swift 闭包表达式
点击右上角即可分享
微信分享提示