React Hooks: useState

本文详细介绍了React Hooks中的useState Hook,包括其基本使用、初步实现、改进完善,以及在实际使用中可能出现的问题。通过示例代码,解释了useState如何保存和更新状态,并强调了在条件语句中使用useState的潜在风险。同时,文章还讨论了useState处理复杂数据类型的方法,并提供了关于状态更新和函数组件特性的深入理解。
摘要由CSDN通过智能技术生成

每天对自己多问几个为什么,总是有着想象不到的收获。 一个菜鸟小白的成长之路(copyer)

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。


useState的基本使用

import React, { useState } from 'react';
import ReactDOM from 'react-dom';
function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>点击</button>
    </div>
  );
}

function render() {
  ReactDOM.render(<Example />,document.getElementById('root'))
}

render()

上面的代码中,定义一个函数组件,名为Example,通过调用 useState Hook 声明了一个新的 state 变量。它返回一对值给到我们命名的变量上。count记录点击的次数,setCount更新count的函数。

useState函数应该具备以下三个功能:

  1. 接受一个参数
  2. 返回一个数组,第一个值为最新的状态值(count),第二个参数为改变状态值的函数(setCount)
  3. 执行改变状态值的函数后需要重新渲染

useState的初步实现

根据上面useState的三个功能,我们可以简单的写个useState函数。

let _state;
function useState(initState) {
  _state = _state || initState
  function _setState(newState) {
    _state = newState
    render()
  }
  return [_state, _setState]
}

分析代码:

为什么在函数的外面定义一个变量? 原因就是每次执行render之后,就会重新执行一遍useState函数,那么就会造成重新赋值,造成没有缓存的效果。

useState内部定义一个函数,来改变_state的值,并且触发render函数。

聪明的人,就会发生上面的代码是存在问题的。上面的代码只是针对一个useState,是没有问题的。但是如果有多个useState,就会造成后面的useState 会覆盖前面的useState。

let _state;   //useState中保存的值的变量

function Example() {
    const [count, setCount] = useState(0)
    const [name, setName] = useState('james')
}
// 执行第一个useState: _state 保存的是count的值
// 执行第二个useState: _state 就是保存的name的值咯

后面的useState,覆盖了前面的useState保存的值,导致一个useState就是出于无效的状态。所以,就需要对代码进一步的改正。


useState的改进完善

let memorialArr = []       //具有记忆的数组
let index = 0              // 定义初始化的下标
function useState(initState) {
  let currentIndex = index  //保存当前的index
  memorialArr[currentIndex] = memorialArr[currentIndex] || initState
  function _setState(newState) {
    memorialArr[currentIndex] = newState
    render()
  }
  index += 1      //index
  return [memorialArr[currentIndex], _setState]
}

function render() {
  index = 0
  ReactDOM.render(<Example />,document.getElementById('root'))
}

代码实现思路:

  • 在外面定义一个数组,用来保存各个useState的值,
  • 定义 index来表示当前是第几个useState
  • useState根据当前的索引值保存在数组中
  • 每次调用useState,就把 index 加 1,为了下次的useState的索引值有所不一样
  • 返回值也是根据当前的索引来进行返回的
  • 最后一点的就是(重要):在重新进行render的时候,把index的值改为0,这样保证useState重新渲染的时候,索引值依然一一对应

上面的最主要的思想就是:保证每个useState的数组对应的index保持一致。

const [count, setCount] = useState(0)
const [name, setName] = useState('james')

无论渲染多少次count对应的索引值为0, name对应的索引值为1

这也能充分的解释 hooks 为什么不能处在if while等判断中(超级重要

function Example() {
    const [count, setCount] = useState(0)
    if(count === 0) {
        const [name, setName] = useState('james')
    }
    const [num, setNum] = useState(0)
    
}
  • 在初始化渲染的时候,memorialArr = [0, 'james', 0]
    • count 对应的index为 0,name对应的index为 1,num对应的index 为2
  • 在第二次渲染的时候,count的条件不满足,那么不会执行useState('name')
    • count对应的index为0,num对应的值就为1

通过上面的分析,在判断中使用useState时,当条件不满足时,就会造成useState对应的index不一致,就会造成取指的混乱,从而报错.


额外补充

1、修改之后,拿不到最新的值

function Example() {
  const [count, setCount] = React.useState(0)

  const btn = () => {
    setCount(count + 1)
    console.log(count);   // 0
  }

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={btn}>count</button>
    </div>
  );
}

​ 在上面的代码中,已经修改了count的值,为什么打印的时候还是0, 这就涉及到了 函数组件特性的 capture value特性。每次修改count的之后,会创建一个新的render状态但是打印count的代码是在上个render状态中,上个render的状态中的count为0,所以打印出0.

  const btn = () => {
    setCount(count+1)
    setCount(count+2)
  }

执行上面的代码,界面上只是显示2, 而不是3? 按照理想的状态: 0 + 11 + 2, 那么最后的结果应该是3。但是实际上,并不是这样。这里还是涉及到了capture value特性。这里修改两次count的值,那就会生成两次新的render状态, 这两次新的render状态都是在最开始render状态基础上新增的,最开始的基础上 render中的 count 为 0,渲染最后一次render状态,所以状态为2

2、useState的函数形式的写法

setCount((prev) => {
    return prev + 1
})

//简写为
setCount(prev => prev + 1)

3、useState处理复杂数据类型

修改的时候,一定要注意,返回一个新的地址,界面才会更新

//处理数组
const [list, setList] = useState(['james', 'kobe'])

//函数形式修改
setList((prev) => {
    prev.push('aa')
    return [...prev]
})
//直接修改
list.push('aa')
setList([...list])
//处理对象
const [obj, setObj] = useState({name: 'james'})

//函数形式修改
setObj((prev) => {
    return {
        ...prev,
        name: 'kobe'
    }
})
//直接修改
setObj({name: 'kobe'})

参考资料:

React Hooks 详解之 useState - 知乎 (zhihu.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值