【React Hooks系列】之useState

3,190 阅读3分钟

前言

由于React的函数式组件使用起来方便(对比class组件),我将重点使用函数组件来运行开发。在这系列博客中,我将分享我所学到Hook系列API的知识。

Hooks系列主要分以下内容:

从简单示例开始

export default function App() {
  const initValue = 0;
  const [n, setN] = useState(initValue);
  const add = () => {
    setN(n + 1);
  };
  return (
    <div className="App">
      <button onClick={add}>+1,此时n:{n}</button>
    </div>
  );
}

上面定义了一个button,当点击时,n+1并更新视图。

useState用法

const [state, setState] = useState(initValue) useState接收一个初始值,这个初始值可以是对象,也可以是简单数据类型。

在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initValue) 值相同。

返回的setState是一个函数,它接收一个新的 state 值并将组件的一次重新渲染加入队列。

规则

只能React函数组件中使用Hooks。

useState不能在内部的循环、条件判断、嵌套的方法中使用。

setState异步更新

接上面的代码

export default function App() {
  const initValue = 0;
  const [n, setN] = useState(initValue);
  const add = () => {
    setN(n + 1);
 +  console.log(n)
 +  setN(n+1)
 +  console.log(n)
  };
  return (
    <div className="App">
      <button onClick={add}>+1,此时n:{n}</button>
    </div>
  );
}

点击之后视图上只加了1,但是log了两次,说明setN是异步的,不能立即更新,而是把所有同步代码执行完之后,再执行视图更新,而这时候入参的n还是旧的值,并没有被更新。

注:这时候传入的 n 由于闭包的原因,所以拿到的是旧的值。比如刚开始 n 是0,调用两次 setN 后相当于设置了两次1。

在React 事件中,setState 确实是异步的,为了提高性能(每次 setState 都会重新执行 hooks 函数),所以相同的setState会进行合并。

setN传函数

setN是可以传递函数的,它的参数是旧的n,返回新的value

上面的代码如果修改成以下内容就有效

export default function App() {
  const initValue = 0;
  const [n, setN] = useState(initValue);
  const add = () => {
  +   setN((oldN) => {
  +   	console.log(oldN);
  +   	return oldN + 1;
    });
  +  setN((oldN) => {
  + 	console.log(oldN);
  +  	return oldN + 1;
    });
  };
  return (
    <div className="App">
      <button onClick={add}>+1,此时n:{n}</button>
    </div>
  );
}

如果可以的话,推荐优先使用函数。

state可以是对象

当 useState 中的 state 为对象时,调用相应的 setState 有一些要注意的地方,useState 不会自动合并更新对象,你可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果。

错误示例

export default function App() {
  const initValue = { n: 0, m: 0 };
  const [state, setState] = useState(initValue);
  const addN = () => {
    setState((state) => {
      return { n: state.n + 1 };
    });
  };
  const addM = () => {
    setState((state) => {
      return { m: state.m + 1 };
    });
  };
  return (
    <div className="App">
      <button onClick={addN}>+1,此时n:{state.n}</button>
      <button onClick={addM}>+1,此时m:{state.m}</button>
    </div>
  );
}

这里的state不会自动帮我们合并,所以需要我们使用...运算符来帮助我们手动合并。

正确示例

export default function App() {
  const initValue = { n: 0, m: 0 };
  const [state, setState] = useState(initValue);
  const addN = () => {
    setState((state) => {
   +  return { ...state, n: state.n + 1 };
    });
  };
  const addM = () => {
    setState((state) => {
   +  return { ...state, m: state.m + 1 };
    });
  };
  return (
    <div className="App">
      <button onClick={addN}>+1,此时n:{state.n}</button>
      <button onClick={addM}>+1,此时m:{state.m}</button>
    </div>
  );
}

总结

  • useState返回一对数组,第一个值是初始值,第二个值state的setter方法
  • 如果state是对象的话,需要我们手动合并
  • setState时,可以传函数也可以传值,如果依赖于旧的state,最好使用函数的形式

参考文档

阮一峰Hooks入门

CS_Joe 掘金分享