React复习回顾

双向绑定和useState的关系

下面是一个双向绑定的手写实现版本。

当我们使用useState的时候直接修改state是不会触发UI的重新渲染的,我们必须使用setState来更新state,因为setState会拦截我们的set操作并重新触发UI的渲染。React具体的做法是批量更新的,每次使用setState的时候会将当前组件标记为脏组件,并将状态放入到一个新的队列中,当当前执行栈为空的时候,批量处理队列中的状态,比较当前DOM和更新DOM之间的差异,进行批量更新。Vue的双向绑定是通过类似下面的方式来实现的,一方是通过获取绑定事件的数据,另一方面是通过数据劫持。

xml 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>双向绑定</title>
</head>

<body>
    <div>
        <span id="display">12312</span>
    </div>
    <input id="input"/>
    <script>
        window.onload = () => {
            // 双向绑定第一部分:UI改变引起数据改变
            const span = document.getElementById("display");
            const input = document.getElementById("input")
            /*使用oninput浏览器事件捕获标签内容的变化然后赋值给我们的data数据 这样也就实现了UI改变引起数据的改变*/
            input.oninput = function(e){
                console.log(e.target.value)
                // span.textContent=e.target.value
                data.value=e.target.value
            }
            // 双向绑定第二部分:数据改变引起UI改变
            /* 这里主要使用了数据劫持,通过使用Object.defineProperty 给我们要改变的数据重写get和set方法,我们需要在set方法中重新
            实现更新的逻辑,主要是加上更新UI的操作这样我们在修改数据的时候UI也会同步修改。这和useState是一样的,当useState中的state
            改变的时候setState也会有一个更新UI的操作,所以我们一般说useState会触发UI的重新渲染。*/
            let data = {
                data:""
            }

            function defineReactive(obj,key,value){
                let internalValue = value;

                const getter = ()=> internalValue

                const setter =(newVal)=>{
                    internalValue=newVal;
                    updateUI()
                }
                Object.defineProperty(obj,key,{
                    get:getter,
                    set:setter

                })

            }
            defineReactive(data,'value','')
            function updateUI(){
                input.value=data.value
                span.textContent=data.value
            }
            setInterval(() => {
                data.value = new Date(); // 这会自动更新UI
            }, 1000);

        }


    </script>
</body>

</html>

setState每次都会重新渲染UI吗

这是不一定的,如果我每次set的都是同一个值,值没有变化,那就不会更新UI,比如setCount(2),这就是说我每次都将count设置为2,重复设置不会更新UI。

只改变对象的属性值不会触发重新渲染UI,具体的代码可以参看下面:

javascript 复制代码
function DiyPage01 (){
    const [person,setPerson] = useState({name:"猪八戒",age:3000})
    const changePerson = function(){
        person.name="孙悟空"
        setPerson(person)
    }
    return (
        <>
            <div>这是diy01页面</div>
            <div>{person.name}--{person.age}</div>
            <Button onClick={changePerson}>按钮</Button>
        </>
    )
}

如果想要改变state之后重新渲染UI,可以用下面的方法:浅拷贝

ini 复制代码
const changePerson = function(){
    person.name="孙悟空"
    const person2 =Object.assign({},person) //浅拷贝
    setPerson(person2)
}

当然ES6中的解构复制也可以帮我们完成浅拷贝,代码如下:

php 复制代码
const changePerson = function(){
    setPerson({...person,name:"孙悟空"})
}

setState异步更新

scss 复制代码
//点击按钮count只会+1,因为三个setCount中的count并非最新的count
const changeCounter = function(){
    /*设置count的时候如果需要用到旧的count的时候需要注意,
    我们的修改可以失效,因为下面的多个count都是同一个count,
    并不是使用上次更新完毕的count*/
    setCount(count+1)
    setCount(count+1)
    setCount(count+1)
}
//点击下面的按钮会+3,回调函数的形参是React传递的当前最新的count
const changeCounter = function(){
    /* 当我们传入一个回调函数的时候,React会将最新的state值作为参数传递给回调函数,
    所以回调函数里面拿到的count是最新的*/
    setCount((count)=>count+1)
    setCount((count)=>count+1)
    setCount((count)=>count+1)
}

上面三次count+1最终count是1而不是3的原因如下:

  • changeCounter 函数内部,count 是一个闭包变量 ,它的值在函数执行时就已经确定了(即当前的 count 值)。
  • 即使你调用 setCount(count + 1) 三次,每次的 count 都是同一个初始值 ,而不是前一次 setCount 更新后的值。

useRef相对于自定义的对象做了什么优化

我们在使用useRef可以将一个DoM元素绑定到我们创建的useRef变量上,这样我们就可以通过useRef变量来操作DOM。除此之外我们还可以使用一个自定义的对象,并将这个对象绑定在DOM上。但是自己创建的对象会在每次渲染的时候重新新建对象并赋值,这样就有一定的性能开销。使用useRef可以避免这种开销。

csharp 复制代码
// 方法一:页面渲染不会改变divRef的值
const divRef = useRef();
// 方法二:重新渲染UI的时候我们创建的对象都要重新重建然后赋值 divRef每次都是一个新值
const divRef = {current:null} 
相关推荐
诗书画唱12 分钟前
JavaScript 基础核心知识点总结:从使用方式到核心语法
开发语言·javascript·ecmascript
水冗水孚1 小时前
通俗易懂地理解深度遍历DFS、和广度遍历BFS
javascript·算法
未来之窗软件服务1 小时前
网页提示UI操作-适应提示,警告,信息——仙盟创梦IDE
javascript·ide·ui·仙盟创梦ide·东方仙盟
爱学大树锯1 小时前
【Ruoyi 解密 - 09. 前端探秘2】------ 接口路径及联调实战指南
前端
老华带你飞1 小时前
校园二手书交易|基于SprinBoot+vue的校园二手书交易管理系统(源码+数据库+文档)
java·前端·数据库·vue.js·小程序·毕设·校园二手书交易管理系统
萌程序.1 小时前
创建Vue项目
前端·javascript·vue.js
VT.馒头1 小时前
【力扣】2704. 相等还是不相等
前端·javascript·算法·leetcode·udp
linweidong2 小时前
Vue前端国际化完全教程(企业内部实践教程)
前端·javascript·vue.js·多语言·vue-i18n·动态翻译·vue面经
lukeLiouu2 小时前
augment不能白嫖了?试试claude code + GLM4.5,十分钟搞定起飞🚀
前端
点正2 小时前
使用 Volta 管理 Node 版本和 chsrc 换源:提升开发效率的完整指南
前端