likes
comments
collection
share

写了两个react hook,大家帮忙品品

作者站长头像
站长
· 阅读数 84

详情

事情是这样的,某天我在参与一个react-native项目开发时,碰到了一个问题:useState更新状态后获取不到最新的状态值,useMemo同样也有这个问题,如下所示:

import { useEffect, useState, useMemo } from 'react'

export default MyComponent = () => {
    const [state, setState] = useState(0)
    const memo = useMemo(() => {
        return state + 100
    }, [state])
    
    useEffect(() => {
        setState(1)
        console.log(state)  // 0
        console.log(memo)   // 100
    }, [])
    
    return (
        <>
            <div>{ state }</div>
            <div>{ memo }</div>
        </>
    )
}

之所以会出现这种情况,是因为setState()这个函数实际上是用新状态去进行另一次渲染,而不会对当前已执行的程序中的状态值造成影响。官方也给出了解决的办法:

写了两个react hook,大家帮忙品品

翻译过来,就是说想要使用新状态的话,可以提前用一个变量去保存新状态。这样useState的问题就解决了,但是useMemo呢 ,useMemo要怎么解决?经过一阵思索后,我决定自己再封装两个hook来解决这个问题。说干就干,在经过一段时间地不停试错和调试后,终于还是被我搞出来了,我欣喜若狂,赶紧拿给同事看看,于是发生了以下对话(手动狗头):

同事:你自己试了吗? 我:试了几次。 同事:感觉怎么样? 我:我修改了一部分用法,但是又保留了一部分,我觉得保留一部分写法习惯,你才知道你在用的是什么hook。

同事试了一下:

import { useEffect } from 'react'
import { useSyncState, useSyncMemo } from 'react-sync-state-hook'

export default MyComponent = () => {
    const [ state, setState ] = useSyncState(0)
    const [ state2, setState2 ] = useSyncState(() => 100)
    const memo = useSyncMemo(() => {
        return state.current + state2.current
    }, [state, state2])
    
    useEffect(() =>{
        setState(1)
        console.log(state.state)     // 0
        console.log(state.current)   // 1
        console.log(memo.state)      // 100
        console.log(memo.current)    // 101
    }, [])
    
    return (
        <>
            <div>{ state.state }</div>
            <div>{ memo.state }</div>
        </>
    )
}

我:现在确实要比原先多写一个.state去调用状态,但其实也是不得已而为之的,主要是为了让useSyncMemo()能更方便地执行监听依赖和同步计算而做出的让步。现在即使是在复杂的循环和递归中,只需调用一次setState(),就能让current自动更新成最新的状态,memo也会随着依赖项的current更新而更新,像这样:

import { useEffect } from 'react'
import { useSyncState, useSyncMemo } from 'react-sync-state-hook'

export default MyComponent = () => {
    const [ state, setState ] = useSyncState(0)
    const memo = useSyncMemo(() => {
        return state.current + 1
    }, [state])
    
    useEffect(() =>{
        for(let i = 0; i < 5; i++){
            setState(i)
            console.log(state.state)     // 0 0 0 0 0
            console.log(state.current)   // 0 1 2 3 4
            console.log(memo.state)      // 0 0 0 0 0
            console.log(memo.current)    // 1 2 3 4 5
        }
    }, [])
    
    return (
        <>
            <div>{ state.state }</div>
            <div>{ memo.state }</div>
        </>
    )
}

我:建立在这种形式的基础上,我们还可以只对current做修改,最后再调用setState()修改状态的值,来避免因频繁修改状态值而增加重新渲染的次数,这样可以带来一点性能上的提升。像这样:

import { useEffect } from 'react'
import { useSyncState, useSyncMemo } from 'react-sync-state-hook'

export default MyComponent = () => {
    const [ state1, setState1 ] = useSyncState([])
    const [ state2, setState2 ] = useSyncState([])

    let request1 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                state1.current = [{x: 1}]
                resolve()
            }, 500)
        })
    }

    let request2 = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                state2.current = [{ y: 2 }]
                resolve()
            }, 1000)
        })
    }

    useEffect(() =>{
        Promise.all([request1(), request2()]).then(res => {
            setState1([{ ...state1.current[0], ...state2.current[0] }])
            setState2(state2.current)
            // 这里虽然setState了两次,但是react会合并所有setState,只发起一次重新渲染。
            // 如果是在request中直接setState则会重新渲染两次
        })
    }, [])

    return (
        <>
            <div>{ state1.state }</div>
            <div>{ state2.state }</div>
        </>
    )
}

我:useSyncState()和setState()的参数形式也和原来的hook是一样的,可以传值也可以传一个函数,用法与原来的hook几乎是一样的,可以自己试试。

总结

useSyncState()和useSyncMemo()的封装、优化逻辑完全符合react官方的要求,并且维持了与原来hook一样的写法,但在用法上略有不同,是通过state.state调用状态以及state.current调用当前状态值,基于这种形式,你将不需要再担心复杂的状态数据流动问题,可对代码逻辑带来一些优化。有需要的小伙伴可以试试看react-sync-state-hook

写在最后,因为useSyncState()会多带来一个闭包变量state.current,因此虽然state.state和普通状态没什么区别,但如果用不上state.current的话,项目中就还是用useState()定义状态,我自己也是这样使用的。

转载自:https://juejin.cn/post/7209504489010921530
评论
请登录