React难上手原因找到了,原来是因为坑太多了。。。

都说React难上手,那它到底难在哪里?

人们总吐槽React相比Vue上手难度难不少,学Vue的可以立即上手,而React则要个十天半个月才能上手写点东西,而且还容易出错。那么React到底难在哪里呢?

React难就难在开发过程中,太容易遇到坑了,需要小心翼翼避开他们,才可以走的顺。

今天我们就来盘点下React开发过程中常见的一些坑吧。

React第一坑

setState异步化这个是很多文章都会提的一个坑,我们来看看它坑在哪里呢?

jsx 复制代码
import { useState } from "react";
export default function Counter() {
  const [count, setCount] = useState(0);
  const onClick = () => {
    setCount(count + 1);
    // 这里打印count
    console.log(count)
  };
  return (
    <div>
      <button onClick={onClick}>点我</button>
    </div>
  );
}

上面的代码点击一次"点我"按钮的时候,控制台会输出啥?

答案是:0。

sateCount并不是立即执行的,因此设置后立即获取count,获取到的还是原来的值。要获取到第二次的状态,需要等待Counter构造函数的下一次执行。

jsx 复制代码
import { useState } from "react";
export default function Counter() {
  const [count, setCount] = useState(0);
  // 这里打印count
  console.log(count)
  const onClick = () => {
    setCount(count + 1);
  };
  return (
    <div>
      <button onClick={onClick}>点我</button>
    </div>
  );
}

也就是想上面的代码一样,点击"点我"按钮后,控制台将输出1。

React第二坑

多次执行setCount,就像执行了一次。我们先看一个代码:

jsx 复制代码
import { useState } from "react";
export default function Counter() {
  const [count, setCount] = useState(0);
  const onClick = () => {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
  };
  return (
    <div>
      <button onClick={onClick}>点我</button>
      当前计数:<span>{count}</span>
    </div>
  );
}

大家看看,当点击"点我"按钮时,当前计数显示的是多少?有人可能会说是:3。

那就错了,答案其实是1。这跟我们的预期有点不一致,这就是它的第二个坑。那如果要达到预期,我们应该怎么改呢?

jsx 复制代码
import { useState } from "react";
export default function Counter() {
  const [count, setCount] = useState(0);
  console.log(count);
  const onClick = () => {
    // 将count改成一个函数(count)=> count + 1
    setCount((count) => count + 1);
    setCount((count) => count + 1);
    setCount((count) => count + 1);
  };
  return (
    <div>
      <button onClick={onClick}>点我</button>
      当前计数:<span>{count}</span>
    </div>
  );
}

有一个奇怪的地方是,我们点击按钮时虽然调用了三次setCount,但是控制台只打印了最后一次的count结果,这是为啥呢?其实是因为setCount是设置后,组件的渲染通过任务队列比如Promise.then或者setTimeout的方式去异步更新的,在队列里面只是判断是否更新过state属性,更新过了才会重新渲染组件,因此多次修改属性后,组件仅仅只会有一次更新,而不是每次修改状态都更新。因此就算像下面的代码,也会导致组件重新渲染:

jsx 复制代码
import { useState } from "react";
export default function Counter() {
  const [count, setCount] = useState(0);
  console.log(count);
  const onClick = () => {
    // 先+1后-1,相当于没改
    setCount((count) => count + 1);
    setCount((count) => count - 1);
  };
  return (
    <div>
      <button onClick={onClick}>点我</button>
      当前计数:<span>{count}</span>
    </div>
  );
}

React第三坑

先看代码

jsx 复制代码
import { useEffect, useState } from "react";
export default function HoverSwitch() {
  const [isOn, setIsOne] = useState(false);
  const label = isOn ? "关闭" : "开启";
  const onClick = () => {
    setIsOne(!isOn);
  };

  useEffect(() => {
    const onMouseMove = () => {
      if (isOn) {
        console.log("move");
      }
    };
    document.addEventListener("mousemove", onMouseMove);
    return () => {
      document.removeEventListener("mousemove", onMouseMove);
    };
  }, []);
  return (
    <div>
      <button onClick={onClick}>{label}</button>
    </div>
  );
}

点击"开启"按钮后,在页面上面移动鼠标,控制台是否会打印"move",答案明显是不会。那究竟是为什么呢?

原因是useEffect函数没有依赖任何state,其回调函数仅仅只会调用一次,虽然该回调函数保存了isOn这个闭包,但是因为isOn是一个基本数据类型,当组件重新渲染时,此isOn已经不是原来的isOn,而useEffect函数保留的还是原来的isOn,所以其值一直都是false,故不会打印"move"。

那要如何做呢?有两个修改方案,一是useEffect的依赖列表上加上isOn;二是isOn改成useRef,即引用类型。我更推荐方案二,因为不用重新执行回调函数,资源消耗更少。

jsx 复制代码
import { useEffect, useState, useRef } from "react";
export default function HoverSwitch() {
  const isOnRef = useRef(false);
  const [label, setLabel] = useState("开启");
  const onClick = () => {
    isOnRef.current = !isOnRef.current;
    setLabel(isOnRef.current ? "关闭" : "开启");
  };

  useEffect(() => {
    const onMouseMove = () => {
      if (isOnRef.current) {
        console.log("move");
      }
    };
    document.addEventListener("mousemove", onMouseMove);
    return () => {
      document.removeEventListener("mousemove", onMouseMove);
    };
  }, []);
  return (
    <div>
      <button onClick={onClick}>{label}</button>
    </div>
  );
}

但是useRef也使用的限制,一是修改ref的值不会触发重新渲染;二是ref的值在渲染期间不可读取。

jsx 复制代码
import { useRef } from "react";
export default function HoverSwitch() {
  const labelRef = useRef('开启');
  // 不能组件的构造函数内容调用labelRef.current,包括下面的div里面
  return (
    <div>
      <button onClick={onClick}>{labelRef.current}</button>
    </div>
  );
}

以上就是我在使用React的过程中总结的一些坑,大家尽量避一避。

相关推荐
Avan_菜菜4 小时前
AI 能写代码了,为什么我反而开始要求它先写文档?
前端·github·ai编程
爱勇宝8 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒11 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen11 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
牧艺12 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
红尘散仙13 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust
袋鼠云数栈UED团队13 小时前
一套 Spec-First 的 AI 编程工作流
前端·人工智能
袋鼠云数栈前端13 小时前
一套 Spec-First 的 AI 编程工作流
前端·ai+
angerdream13 小时前
Android手把手编写儿童手机远程监控App之vue3 路由守卫
前端
不服老的小黑哥14 小时前
AI规范驱动编程-harness工程项目实战
前端