React Hooks之useState/useReducer(手写useState)

手写useState:

import ReactDOM from "react-dom";
import React from "react";

let _state = []
let index = 0
const myUseState = (initialState) => {
    const currentIndex = index
    _state[currentIndex] = _state[currentIndex] === undefined ? initialState : _state[currentIndex]
    const setState = (newState) => {
        _state[currentIndex] = newState
        render()
    }
    index ++
    return [_state[currentIndex], setState]
}

const render = () => {
    index = 0
    ReactDOM.render( <App/>,document.getElementById('root') );
}

const App = () => {
    const [n, setN] = myUseState(0)
    const [m, setM] = myUseState(0)
    return <>
            <div>
                n:{n}
                <button onClick={() => {setN(n + 1)}}>n+1</button>
            </div>
            <div>
                m:{m}
                <button onClick={() => {setM(m + 1)}}>m+1</button>
            </div>
        </>
}
export default App

总结:

  • 每个函数组件对应一个React节点
  • 每个节点保存着state和index
  • useState会读取state[index]
  • index由useState出现的顺序决定
  • useSate有固定的出场顺序:
    例如第一次渲染时,n是第一个,m是第二个,k是第三个;
    那么第二次渲染时,必须保证完全一致的顺序;
    所以useState不能放在条件判断里面去决定是否渲染。
  • setState会修改state,并触发更新
  • state每一次的改变都是一个新的state (但vue3没有产生新的state) =>useRef就是一个状态贯穿始终

以上解释是做了简化,React节点为FiberNode,_state的名称为memorizedState,index的实现则用到了链。

一个小例来说明产生了新的n:

const App = () => {
    const [n, setN] = React.useState(0)
    const onClickN = () => {
        setTimeout(() => {
            console.log(n);
        }, 3000)
    }
    return <div>
        <div>n:{n}</div>
        <button onClick={()=>setN(n+1)}>n+1</button>
        <button onClick={onClickN}>点击,3秒后打印出n</button>
    </div>
}

以上代码,两个按钮点击的先后顺序不用会打印出不同的结果哦

  • 先点击'n+1'按钮,后点击'3秒后打印出n'按钮,3秒后打印出1,因为这时UI已经更新,打印出新的n
  • 先点击'3秒后打印出n'按钮,后点击'n+1'按钮,3秒后打印出0,虽然这时UI已经更新,产生新的n:1,但指定打印的是旧的n,setN后得到的是新的n,新的n和旧的n是同时存在的。

setState注意事项

  1. state为对象时,不可局部更新,用以下方法解决:
function App(){
    const [user, setUser] = React.useState({name:'fanlelee',age:18})
    const onClick = ()=>{
        setUser({     //给一个全新的对象
            ...user, //先复制一份原来的数据
            name:'Lisa'
        })
        
        /*** 错误写法!!***/
        user.name='Lisa'
        setUser(user) //因为对象地址没有改变,React就认为数据没有变化
        /*******/
    }
    return (
        <div>
            <button onClick={onClick}>Click</button>
        </div>
    )
}

因为setSate不会帮我们合并属性,另外useReducer也不会合并属性!!!

  1. state如果是一个对象,setState()里面必须是一个新的对象,因为setState如果对象地址不变,那么React就认为数据没有变化。

  2. setState接受函数(多次操作setState)

function App(){
    const [n, setN] = React.useState(0)
    const onClick = ()=>{
    
        /*** 错误写法!!***/
        setN(n+1)
        setN(n+1) //n不会+2,只会+1
        /*******/
        
        //setN接受函数:
        setN(x=>x+1)
        setN(y=>y+1)
        //以上,得到的结果是+2
    }
    return (
        <div>
            n:{n}
            <button onClick={onClick}>Click</button>
        </div>
    )
}

useState注意事项

  • useState()里面的初始值可以是一个函数,该函数返回一个对象
    因为有时候对象里面的值比较复杂,需要计算得到,把它写成一个“返回对象的函数”可以减少计算过程;
    例:
useState({name:'Lisa',age:9+9}) //js引擎会去解析这个对象
useState(()=>({name:'Lisa',age:9+9})) //js引擎是不会立即执行函数的
  • 当你发现用了多个useState的变量应该放在一个对象里面,这个时候可能用useReducer更合适:
    a. 所有在useState里面的规则在useReducer里面是一样的;
    b. 总的来说useReducer就是useState的升级版;
    c. useReducer就是把参数的操作集中写在了reducer里面;
    d. useReducer的好处就是用来践行Flux/Redux的思想。
const initialUser = {
    name: 'fanlelee',
    age: 18
};

function reducer(state, action) {
    switch (action.type) {
        case 'addAge':
            return state = {...state, age: state.age + 1};
        case 'changeName':
            return state = {...state, ...action.data};
        case 'reset':
            return initialUser;
        default :
            throw new Error('unknown type')
    }
}

function App() {
    const [user, dispatch] = useReducer(reducer, initialUser);
    return (
        <div>
            <div>
                姓名:
                <input type="text" value={user.name}
                       onChange={(e) => {
                           dispatch({
                                type: 'changeName', 
                                data: {name: e.target.value}})
                       }}
                />
            </div>
            <div>
                年龄:{user.age}
                <button onClick={(e) => { dispatch({type: 'addAge'}) }}>
                    +1
                </button>
            </div>
            <div>
                <button onClick={(e) => { dispatch({type: 'reset'})}}>
                    重置
                </button>
            </div>
            {JSON.stringify(user)}
        </div>
    );
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容