前言
推荐一本书learn react hooks,这本书详细介绍了useState的原理,然后通过一个博客例子介绍了常用的几个hooks、React.lazy、react-router以及自定义hooks。目前没有中文版,不过有代码。鄙人是个英文渣,不过功夫不负有心人,有个app叫拍读英语,可以拍照翻译,对照着代码可以一章一章看下去。这本书的经典之处是作者实现了一个useState,通过这个介绍官方的useState为什么那么奇怪,为什么不能在条件语句中使用hooks。
useState的奇怪之处
useState的参数只在初次调用时起作用(存储)
举个例子:
import React from "react"; import Bar from './Bar.js' import "./styles.css";
export default function App() { const [value,setValue] = React.useState(0) return ( <div className="App"> <button onClick={e=>{ setValue(value+1) }}>add</button> <Bar value={value}/> </div> ); }
|
Bar.js
import React from 'react'
const Bar = (props)=>{ const result = React.useState(props.value)[0] console.log('render',result) console.log('props',props.value) return ( <div> {`result: ${result}`} </div> ) }
export default Bar
|
结果:

每次点击add,Bar都会重新render,但是result永远都是0,可事实上props.value变了。这不符合常理。
useState不能放到条件语句中
众所周知,useState是不能放在条件语句中的。
if(xxx){ const [value,setValue] = React.useState('') }
|
如果开发过程中使用eslint这样的工具,是不允许写这样的代码的.
自己实现一个useState
useState的原理
useState是内部是利用数组来存储值的,而且是利用数组的下标来区分不同的值得。如果把useState用在条件语句中,数组的下标就会产生混乱。
初次读这句话我是不理解的,不过看了实现就彻底明白了。
手写useState
高手可以直接看书中的实现,其核心是用了闭包,初次调用useState函数时,同时初始化了hookIndex和setState函数,hookIndex会一直存在于setState的闭包中,当setState被调用时,会通过hookIndex找待更新值的位置,所以hookIndex一旦错误,就会出错。
文字的解释永远是乏味的,如果能看懂代码,根本无需多言。
借用一下书中的实现:
import React from 'react' import ReactDOM from 'react-dom'
let values = [] let currentHook = 0
function useState (initialState) { if (typeof values[currentHook] === 'undefined') values[currentHook] = initialState
let hookIndex = currentHook function setState (nextValue) { values[hookIndex] = nextValue ReactDOM.render(<MyName />, document.getElementById('root')) } // 经典代码,hookIndex默认等于currentHook,但是它不会随着currentHook的变化而变化,在useState执行完毕后一直存在于setState函数的闭包中。setState函数执行完毕后,会重新渲染组件,useState会重新执行,所以hookIndex的值很重要(useState不能写在条件语句中),不然会张冠李戴。 return [ values[currentHook++], setState ] // 我们用的最多的代码,其实最不重要。 }
function MyName () { currentHook = 0
const [ name, setName ] = useState('') const [ lastName, setLastName ] = useState('')
function handleChange (evt) { setName(evt.target.value) }
function handleLastNameChange (evt) { setLastName(evt.target.value) } return ( <div> <h1>My name is: {name} {lastName}</h1> <input type="text" value={name} onChange={handleChange} /> <input type="text" value={lastName} onChange={handleLastNameChange} /> </div> ) }
export default MyName
|
用数组来记录useState的值看起来很奇怪,但却是最好的方法。如果用对象,我们每次调用useState时都必须传一个hookName,而且绝对不能重复,这其实很难。
PS: 最后说一句,虽然我写的很烂,但万一哪位不开眼的copy了我的文章,麻烦注明出处。或者直接买书。