背景

年底了,换了项目组,新的项目组使用react,从vue到react,我只花了一天的时间,看了官方简单的文章之后,就觉得这玩意很简单啊,比起vue的那么api来说,这根本没有学习成本好吧,十分迅速的就进入了react的项目开发,并且洋洋得意,根据我多年的经验来看,这波肯定会得到领导的赏识

很快,我就做完了我的需求,把代码提交上去,组长可能确实比较闲,还review了我的代码,并且指出了一系列的问题,并告诉我说学习react最难的部分,并不是知道怎么使用它,而是要知道怎么能够编写良好,干净的react代码

主要给我提了六点错误,我相信在座的各位,可能需要对号入座

在不需要使用state的时候使用state

涉及到项目中的代码逻辑,我们将一些内容通过demo的形式展示出来

提交表单在很多场景下都需要用到,对于一些表单的提交,大多数人的代码的实现可能是以下方式

export default function App() {
  const [name, setName] = useState("");
  const submit = (e) => {
    e.preventDefault();
    console.log(name);
  };
  const change = (e) => {
    setName(e.target.value);
  };
  return (
    <div className="container">
      <form onSubmit={submit}>
        <label htmlFor="name">姓名: </label>
        <input value={name} onChange={change} type="text" id="name" />
      </form>
    </div>
  );
}

页面上有一个姓名输入框,通过state的方式将数据绑定,提交的时候从state上再把数据取到,这一点确实很像vue的双向绑定,通过state的方式实现了,看着表面没有问题,并且页面也呈现了,submit的数据也取到了

但是实际上,我们并没有在别的地方使用这个name状态,除了在提交的时候,有人会说,value也用到了,但是实际上你是可以不需要value这个字段的,只有提交的时候才会用到这个数据,所以这里完全可以不使用state,防止组件刷新

只需要通过ref改一下即可

export default function App() {
  const nameRef = useRef(null);
  const submit = (e) => {
    e.preventDefault();
    console.log(nameRef.current.value);
  };
  return (
    <div className="container">
      <form>
        <label htmlFor="name">姓名: </label>
        <input ref={nameRef} type="text" id="name" />
      </form>
      <button onClick={submit}>提交</button>
    </div>
  );
}

和之前一样,我们点提交按钮的时候获取到了最新数据,并且页面没有多次刷新

useState的回调函数

那什么情况下使用useState呢?这种在页面上呈现的内容需要使用,比如一个计数器

export default function App() {
  const [count, setCount] = useState(0);
  const submit = (val) => {
    setCount(count + val);
  };
  return (
    <div className="container">
      <button onClick={() => submit(-1)}>-1</button>
      <span>{count}</span>
      <button onClick={() => submit(1)}>+1</button>
    </div>
  );
}

表面看着没有问题,点击加减也都挺正常的,但是如果你是熟悉useState的话,你也会给setCount传递一个函数的形式,这两者表现形式似乎完全一样,感受不到区别

const submit = (val) => {
  setCount((current) => current + val);
};

那具体的区别在哪呢?在不使用回调函数的时候,如果我们连续更新状态的话,像下面这样

setCount(count + val)
setCount(count + val)

实际上页面也只会加一次,因为在这一次的更新过程中,count的值是固定的,也就是我们常说的setState是异步的原因(当你更改状态的时候,它不会立刻更新,而是等到下一次render才会更新),并且react会将state进行批处理,但是如果是函数的形式

const submit = (val) => {
  setCount((current) => current + val);
  setCount((current) => current + val);
};

就能够得到想要的结果,所以如果你想使用之前的状态来进行state值的修改,最好使用函数形式

state异步更新,useEffect的使用

通过上一个count,我们知道我们立刻获取count值的时候获取到的不是最新值

const submit = (val) => {
    setCount((current) => current + val);
    console.log(count);
  };

这时候有人就想到了useEffect,如此就获取到了新的值

useEffect(() => {
  console.log(count)
}, [count])

然后对于这一点,很多同学就将useEffect当成了vue中的watch,实现了下面的逻辑

export default function App() {
  const [user, setUser] = useState("");
  const [name, setName] = useState("");
  const [userName, setUserName] = useState("");

  useEffect(() => {
    setUserName(user + name);
  }, [user, name]);

  return (
    <div className="container">
      <input
        type="text"
        value={user}
        onChange={(e) => setUser(e.target.value)}
      />
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <span>{userName}</span>
    </div>
  );
}

咋一看好像没什么问题,并且实现了对应的功能,但是它不是最佳方式

实际完全可以不用userName这个state,直接在dom中<span>{user + name}</span>就能实现对应的效果

有时候很多同学也会使用useEffect进行下面的操作

useEffect(() => {
  fetch('#').then(d => setData(d))
}, [])

useEffect(() => {
  console.log(d)
}, [d])

那为什么你不把console.log的逻辑放在.then里面呢?

常见useEffect错误

const [user, setUser] = useState("");
const person = {
  user
}
useEffect(() => {
  console.log(person)
}, [person])

这种情况,大多数会认为useEffect会在组件初始化的时候执行一次,但实际上useEffect中的回调会执行多次,因为person是个引用类型,每次的指针地址都是变化的

这个时候你可以使用useMemo来解决这个问题

const person = useMemo(() => ({
  user
}), [user])

依赖问题

由于业务很复杂,所以在不断迭代的过程中,我们的effect通常就会变成这种情况

useEffect(() => {
 ......
}, [person, name, age, id, status, address ....])

依赖会越来越多,稍微改其中的一个点,就会执行effect大片的逻辑,这里最好能够拆分或者合并,确定要执行一个逻辑的,最好放在一个state中,比如可以将age,name,id,等基础信息放在一个state,然后这个effect依赖这一个state就可以了,进行state合并

总结

上述就是一些基础react使用者常出现的一些问题,hook确实能给我们带来很大的便利,但是有时候从vue到react,其中的一些思想还是需要做一些调整,才能更好的适应hooks的方式,我们可以多看看好的一些hooks的封装,加深一些hooks的理解,也欢迎大家关注公众号【FE情报局】,一起讨论一些技术文章


FE情报局
30 声望5 粉丝