React 状态钩子useState()深度剖析

·  阅读 12
React 状态钩子useState()深度剖析

学习新东西的时候,首先要问自己两个问题?

    1. 为什么会存在这个东西?
    1. 这个东西能解决什么问题?

1.为什么会存在这个东西?

react的核心是组件。v16.8版本之前,组件的标准写法是类(class)。

大型组件很难拆分和重构,也很难测试;业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑;组件类引入了复杂的编程模式,比如 render props 和 高阶组件。

react团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。组件的最佳写法应该是函数,而不是类

但是函数组件有重大限制,必须是纯函数,不能包含状态,也不支持生命周期方法,因此无法取代类。

`react hooks 的设计目的,就是加强函数组件,完全不使用类,就能写出一个全功能的组件`
复制代码

2.这个东西能解决什么问题?

前言

由于大型组件类的难拆分性,重复逻辑和复杂的编程模式。编写函数组件好拆分、可复用性强。但是有限制性局限性。所以在函数组件中,如果需要外部功能和副作用,就需要钩子把外部代码“钩”进来。

概念

useState是一个Hook函数,让你在函数组件中拥有state变量。它接收一个初始化的state,返回是一个数组,数组里有两个元素,第一个元素是当前状态值和另一个更新该值的方法。

用法

const [state, setState] = useState(initialState)
复制代码

规则

useState 和所有hooks一样,都遵循同样的规则:

  • 在顶层调用hooks。不能在循环、条件或者内嵌方法中调用hooks。因为react依赖被调用的顺序来获取特定变量的正确值。
  • 在函数组件中调用hooks,不要在类组件中使用hooks.

案例1

基础使用
const Parent = () => {
    const [count, setCount] = useState(0)
    
    return (
        <>
          <h1>parent</h1>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>Click me</button>
          
          <div>--------------------</div>
          
          <Child count={count} setCount={setCount} />
        </>
    )
}


const Child = ({ count, setCount }) => {
  return (
    <>
      <h1>child</h1>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </>
  )
}
复制代码

Kapture 2022-07-19 at 11.36.39.gif

更新方法不会立即更新值

如果你需要用到之前的值更新状态,你必须将之前的值传递给该方法,则它会返回一个更新后的值,eg: setMessage(previousVal => previousVal + currentVal)

function Case2 () {
  const [count, setCount] = useState(0);
  const [count1, setCount1] = useState(0);

  function A () {
    setCount(count + 1)
    setCount(count + 1)
  }

  function B () {
    setCount1(pre => pre + 1)
    setCount1(pre => pre + 1)
  }

  return <div>
    <h1>double click</h1>
    <h2>count + 1</h2>
    <p>You clicked {count} times</p>
    <button onClick={() => A()}>click</button>
    <div>--------------------------</div>
    <h2>prev => prev + 1</h2>
    <p>You clicked {count1} times</p>
    <button onClick={() => B()}>click</button>
  </div>
}
复制代码

Kapture 2022-07-19 at 11.42.17.gif

初始值

useState接收状态的初始值作为一个参数。

初始化的值仅仅会在第一次渲染时被赋值(如果他是一个函数,也是会在初次渲染时执行)。 在后续的更新中(由于组件本身的状态更改或者是说父组件导致的变化),useStateHook参数(初始值)将会被忽略,当前的值将会被使用。

1.1 初始值通过函数传递

const Message = () => {
    const messageState = useState(() => expensiveComputation())
}
复制代码

1.2 初始值通过组件函数参数传递(props)

const Message = (props) => {
    const messageState = useState(props.message)
}
复制代码
function Case3 () {
  
  function init () {
    return 1
  }

  const [count, setCount] = useState(() => init());

  const increase = () => {
    setCount(count + 1);
  }

  const decrease = () => {
    setCount(count - 1);
  }

  return <div className="container">
    <button className="chevron chevron-up" onClick={ decrease } />
    <h1>{ count }</h1>
    <button className="chevron chevron-down" onClick={ increase } />
  </div>
}
复制代码

Kapture 2022-07-19 at 13.50.14.gif

使用对象作为状态变量
  1. 不可变的重要性
  2. useState返回的更新方法不是像类组件中的setState合并对象
function Case4 () {
  const [state, setState] = useState({ count: 0, name: 'jack' })

  function A () {
    setState(prevState => ({...prevState, count: prevState.count + 1}))
  }

  function B () {
    setState(prevState => prevState.count + 1)
  }

  useEffect(() => {
    console.log(state)
  })

  return <div>
    <h3>{ state.count }</h3>
    <h3>{ state.name }</h3>
    <h2>corret set</h2>
    <button onClick={() => A()}>set</button>
    <h2>wrong set</h2>
    <button onClick={() => B()}>set</button>
  </div>
}
复制代码

Kapture 2022-07-19 at 13.51.54.gif

在某些情况下,拷贝深度内嵌对象是比较昂贵的,因为React可能会依赖那些没有改变过的字段值重新渲染你应用的部分内容。

对于这个原因,首先要做的是尝试扁平化你的对象。需要关注的是,React官方推荐根据哪些值倾向于一起变化,将状态分割成多个状态变量

如果这个不可能的话,推荐使用第三方库来帮助你使用不可变对象,例如immutable.js或者 immer


via1 via2

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改