了解一个框架的最好方式,便是从它的数据开始,无论是vue,亦或者react,都是如此。对于react来说,最重要的数据莫过于状态值state,在官方的新文档中,state被描述为一个组件的记忆,我们可以将其理解为显示在屏幕上的动态数据。
export default function Count(){ let num = 0 const handleClick = () => { num += 1 } return ( <> <div>Count: { num }</div> <button onClick={ handleClick }>+1</button> </> ) }handleClick() 事件处理函数更新了局部变量 num。但存在两个原因使得屏幕上的num不会发生变化:
const [state, setState] = useState(initialState);其中state变量用于保存渲染间的数据,而setState函数则用来更新变量并触发 React 再次渲染组件(即re-render),re-render会再次调用组件函数,并用新状态值替换旧的状态值去执行相应的渲染或操作,我们将 Count 组件用状态改写如下:
export default function Count(){ const [ num, setNum ] = useState(0) // 堆代码 duidaima.com const handleClick = () => { setNum(num + 1) } return ( <> <div>Count: { num }</div> <button onClick={ handleClick }>+1</button> </> ) }
现在屏幕上的num就能实现动态变化了。
let [state, setState] = useState(initialState);或者这样:
const stateArr = useState(initialState);
react并没有严格限制这些写法,但是它们都是不规范的写法,这里就不得不谈及状态的不可变性了。在react的每一次组件渲染切片中,其内部的状态值在其渲染到屏幕上之前的那一刻,就已经固定下来了,后续无论什么样的操作,我们都不应该再对其造成影响,为什么要这么做呢?我们知道动画是由一帧帧快照组成的,react也是如此,在react中,每一次渲染就如同一帧快照切片,这样的每一帧快照,它的状态就必须自始至终保持不变,以便我们可以随时进行Time Travel,也不会出现前后渲染不一致的问题。
const [state, setState] = useState([ { a: 0 }, { b: 1 } ]); // 堆代码 duidaima.com // 不规范写法 const s = [...state] s[0].a = -1 setState(s) // 规范写法 const s = [...state] s[0] = { ...s[0], a: -1 } setState(s)
维持状态不可变性的最好方法便是拷贝,如何以最低的成本去进行拷贝,需要我们自己去衡量把握。