react 进阶hook 之 State Hook

简介: 在类组件中,使用 this.setState({a:xxx}) 是会对当前类实例的属性进行混合。但是在state hook 中是不会进行混合的,原因嘛: 官方就是要让你你定义多个 hooks, 将每一个代码块进行分离,从而降低代码的维护成本。

State Hook


大家都知道,在react的类组件中是可以使用状态的——state,那么在函数组件中怎么使用状态呢?在react16.8的之后中提出了hook这个概念。State Hook是一个在函数组件中使用的函数(useState),用于在函数组件中使用状态。并且使用起来会让人感觉神清气爽。


语法


const [a, setA] = useState('参数')


  • useState函数有一个参数,这个参数的值表示状态的默认值。用法和vue3的ref('')一样,可以传递任何数据。但是vue3会变成响应式的数据,而react state hook 是对外提供了两个返回值,可以对数据进行操作(get, set)
  • 函数的返回值是一个数组,该数组一定包含两项

。第一项:当前状态的值

。第二项:改变状态的函数


注意: 一个函数组件中可以有多个状态,这种做法非常有利于横向切分关注点。


案件


import React, { useState } from 'react'
export default function TestStateHook() {
// 通过数组解构的方式来进行获取useState的返回值
  const [data, setData] = useState(0)
  return (
    <div>
      <button onClick={() => {
        setData(data - 1)
      }}> - 1 </button>
      <span> {data} </span>
      <button onClick={() => {
        setData(data + 1)
      }}> + 1 </button>
    </div>
  )
}


效果


20210331192840906.gif


我们在效果中发现,state Hook 中实现了和 class组件一样的效果,并且在调试工具中,也是可以看到hooks中的值的。


原理


在外部使用一个数组来接收所有的state 的状态值。然后最外暴露属性值和一个函数,相当于一个是getter 和 setter


手动实现


let memoizedState = []; // hooks 存放在这个数组
let cursor = 0; // 当前 memoizedState 下标
function useState(initialValue) {
  memoizedState[cursor] = memoizedState[cursor] || initialValue;
  const currentCursor = cursor;
  function setState(newState) {
    // 如果传入的参数没有变化,不进行更新,不调用render函数
    if(newState === memoizedState[currentCursor]) return;
    memoizedState[currentCursor] = newState;
    render();
  }
  return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1
}


详情参考


注意的细节


1. useState最好写到函数的起始位置


原因:便于阅读,和我们定义变量一样,hook出现的目的是为了使代码形成代码块,每一段的逻辑分离开来。


案例


import React, { useState } from 'react'
// 代码写在function外部,是不行的
const [data, setData] = useState(0)
export default function TestStateHook() {
  return (
    <div>
      <button onClick={() => {
        setData(data - 1)
      }}> - 1 </button>
      <span> {data} </span>
      <button onClick={() => {
        setData(data + 1)
      }}> + 1 </button>
    </div>
  )
}


效果


20210331193408467.png


2. useState严禁出现在代码块(判断、循环)中


案列


import React, { useState } from 'react'
export default function TestStateHook() {
  let n = 0;
  if(n > 0){
  // hook 定义在代码判断中
    var [data, setData] = useState(n)
  }
  return (
    <div>
      <button onClick={() => {
        setData(data - 1)
      }}> - 1 </button>
      <span> {data ? data : 0} </span>
      <button onClick={() => {
        setData(data + 1)
      }}> + 1 </button>
    </div>
  )
}


效果


20210331193848490.png


3. useState返回的函数(数组的第二项),引用不变


原因:(节约内存空间)


案例


import React, { useState } from 'react'
export default function TestStateHook() {
  const [data, setData] = useState(0);
  //定义一个数组来装setData 函数
  const arr = [setData]
  return (
    <div>
      <button onClick={() => {
        setData(data - 1)
        arr.push(setData)
      console.log( arr[0] === arr[1]);
      }}> - 1 </button>
      <span> {data} </span>
      <button onClick={() => {
        setData(data + 1)
        arr.push(setData)
      }}> + 1 </button>
    </div>
  )
}


效果


20210403195932991.png


4.使用函数改变数据,若数据和之前的数据完全相等(使用Object.is比较),不会导致重新渲染。


以达到优化效率的目的,自带类组件的纯组件(pureComponent)


案例


import React, { useState } from 'react'
export default function TestStateHook() {
  const [data, setData] = useState(0);
  // 如果传入的值不同,将会允许改函数
  console.log('函数初始化')
  return (
    <div>
      <button onClick={() => {
      // 相同的值不会进行更新
        setData(data)
      }}> - 1 </button>
      <span> {data} </span>
      <button onClick={() => {
        setData(data + 1)
      }}> + 1 </button>
    </div>
  )
}


效果


20210403203007222.gif


5.使用函数改变数据,传入的值不会和原来的数据进行合并,而是直接替换。


在类组件中,使用 this.setState({a:xxx}) 是会对当前类实例的属性进行混合。但是在state hook 中是不会进行混合的,原因嘛: 官方就是要让你你定义多个 hooks, 将每一个代码块进行分离,从而降低代码的维护成本。


案例


import React, { useState } from 'react'
interface obj {
  a?: number,
  b?: number
}
export default function TestStateHook() {
  const [data, setData] = useState<obj>({ a: 123, b: 321 });
  console.log('函数初始化')
  return (
    <div>
      <p>数字A: {data.a} </p>
      <p>数字B: {data.b} </p>
      <button onClick={() => {
        setData({ b: 42344 })
      }}>改变B </button>
    </div>
  )
}


效果


20210403204529319.gif


上面我们看到了,如果和类组件一样A的值是会存在的,但是意料之外。所以如果某些状态之间没有必然的联系,应该分化为不同的状态,而不要合并成一个对象,对于上面代码如何实现才能实现A不变,修改B的值的时候,把data 展开进行混合。在class组件中,我们使用pureComp 就是每一次都需要传入一个不同引用值的数据,从而来进行数据的更新,否则不会对数据进行更新的。详情查看


6.如果要实现强制刷新组件


1.类组件:使用forceUpdate函数, vue2里面也有一个$foreUpdate来强制刷新组件


2.函数组件:使用一个空对象的useState,如: setData({}), 解释一下为啥传入空对象为啥会强制刷新组件,因为传入的值是一个引用值,我们传入一个空对象的引用地址和原来的对象或者啥值都是不一样的。由于是基于改基础的,所以传入一个空的数组或者函数也是可以做到的。有兴趣的读者可以自行尝试。


7. 和类组件的状态一样,函数组件中改变状态可能是异步的(在DOM事件中)。


多个状态变化会合并以提高效率,此时,不能信任之前的状态,而应该使用回调函数的方式改变状态。如果状态变化要使用到之前的状态,尽量传递函数。


案例


在我门最开始的那个案例, 请关注我们react 中的hooks的值,我们会发现他并不是我们点一次就变化一次的,而是在过一段时间后才进行更新的。


20210403205825148.gif


解决办法: 和类组件一样,使用函数来进行更新就可以获取到之前的值。


 setData(s => {
          // 这样就可以获取之前的状态了
          console.log(s)
          return s + 1
        })


相关文章
|
3月前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
91 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
3月前
|
前端开发
学习react基础(2)_props、state和style的使用
本文介绍了React中组件间数据传递的方式,包括props和state的使用,以及如何在React组件中使用style样式。
42 0
|
2月前
|
前端开发 JavaScript 调度
React 组件状态(State)
10月更文挑战第8天
35 1
|
4月前
|
前端开发
React组件实例更改state状态值(四)
【8月更文挑战第14天】React组件实例更改state状态值(四)
53 1
React组件实例更改state状态值(四)
|
4月前
|
前端开发 JavaScript
React组件实例state(三)
【8月更文挑战第14天】React组件实例state(三)
31 1
React组件实例state(三)
|
3月前
|
前端开发
React使用hooks遇到的坑_state中的某几个属性数据变成了空字符
本文讨论了在React使用hooks时遇到的一个问题:state中的某些属性数据变成了空字符。作者通过在修改函数中重新解构赋值来获取最新的state值,解决了因数据更新不及时导致的问题。
88 0
|
4月前
|
存储 前端开发
React 中的 state 和 props 有什么区别?
【8月更文挑战第31天】
56 0
|
4月前
|
前端开发
React 中的 Hook 概念
【8月更文挑战第31天】
41 0
|
5月前
|
存储 前端开发 JavaScript
前端框架与库 - React基础:组件、Props、State
【7月更文挑战第12天】React是JavaScript库,专注UI构建,基于组件化。组件是UI模块,可函数式或类定义。Props是组件间安全传递数据的只读参数,用defaultProps和propTypes保证正确性。State则是组件内部可变数据,用于驱动更新。使用setState()确保正确变更和渲染。了解并妥善处理这些概念是高效React开发的基础。
76 7
|
5月前
|
前端开发
React useImperativeHandle Hook
【7月更文挑战第1天】React useImperativeHandle Hook
32 3