React Hooks(一) useState

一 什么是React Hooks

在讲useState的用法之前,先来看看什么是React Hooks。

hooks其实就是有状态的函数式组件。闭包,是react hooks的核心。

闭包

闭包是一个特殊的对象,由两部分组成,执行上下文的A以及在A中创建的函数B。当执行B时,如果访问了A中的变量对象,那么闭包就会产生。

 // 执行上下文,这里暂时写为一个函数,它也可以指一个模块
 const A = () => {
    
    
   const a = "aaa" 
   const B = () => {
    
     
        // 注意这里可以修改a的值
      console.log(a)  
     // 当B执行时,如果访问了A中的变量,那就产生闭包,这里称A(执行上下文)为闭包
   }
 }

当我们定义一个react组件,并且在其他模块中使用时,就会产生闭包。

// 这里把A.js看成是一个模块
export const A = () => {
    
     
    const a = 'aaa'
 }
// B.js
import {
    
     A } from './A.js'
const B = () => {
    
     
    // 这里把B.js看成是另一个组件,引入A组件,并且访问A组件中的变量,此时形成闭包, B里可以修改a的值
    console.log(a) 
}

根据闭包的特性,A模块中的a变量会持久存在,因此当B函数再次执行时,我们也能获取到上一次B函数执行结束时a的值。
我们可以根据闭包的原理写一个自定义的useState。

// state.js模块 对应上面的A.js
let state = null;

export const useState = (value) => {
    
    
    // 第一次调用的时候没有初始值,因此使用传入的初始值赋值
    state = state || value
    function dispatch(newValue) => {
    
    
        state = newValue
        
    }
    return [state, dispatch]
}

// 在其他模块中引入并使用 对应上面的B.js 相当于是一个hooks组件
import React from 'react'
import {
    
     useState } from './state'

export default function Demo() {
    
     
    const [count, setCount] = useState(0)
    return <button onClick={
    
    () => setCount(count + 1)}>{
    
    count}</button>}

react采用自上而下单向数据流的方式来管理自身的数据和状态,数据只能由父组件触发,向下传递到子组件。所以在react中,state和props的改变都会引发组件重新渲染,如果是父组件的变化,则父组件下面的子组件也会重新渲染。
渲染方式:
class组件:重新执行render方法;
函数式组件:整个函数重新执行;

二 useState()

useState()的主要作用就是保存状态,它接收一个参数就是默认值。它返回一个数组,数组第一个是数据,第二个是操纵数据的方法。
先来看个简单的例子:

import React, {
    
     useState } from 'react';

function Bulbs() {
    
    
  const [on, setOn] = useState(false);

  const lightOn = () => setOn(true);
  const lightOff = () => setOn(false);

  //lightOn和lightOff实际上用lightSwitch一个方法就够了
  const lightSwitch = () => setOn(on => !on);

  return (
    <>
      <div className={
    
    on ? 'bulb-on' : 'bulb-off'} />
      <button onClick={
    
    lightOn}>开灯</button>
      <button onClick={
    
    lightOff}>关灯</button>
    </>
  );
}

1.用法

(1) useState接收一个值作为当前状态的初始值。只在组件首次渲染才会执行。也可以传入函数作为参数。
这里看一个传入函数的例子:

const a = 10
const b = 20
const [count, setCount] = useState(() => {
    
    
    return a + b
})

(2)使用回调更新状态

const [toggled, setToggled] = useState(false);
setToggled(toggled => !toggled);

const [count, setCount] = useState(0);
setCount(count => count + 1);

// 数组的修改要注意
const [items, setItems] = useState([]);
setItems(items => [...items, 'New Item']);

2.要注意的问题

(1)useState()仅顶层调用,仅在函数组件活自定义钩子内部调用。不能在循环,条件,嵌套函数中调用。
(2)过时状态
闭包是从外部作用域捕获变量的函数。由于状态变量在渲染之间变化,因此闭包应捕获具有最新状态值的变量,否则,可能会遇到过时的状态问题。

function DelayedCount() {
    
    
  const [count, setCount] = useState(0);

  const handleClickAsync = () => {
    
    
    setTimeout(function delay() {
    
    
      setCount(count + 1);
    }, 3000);
  }

  return (
    <div>
      {
    
    count}
      <button onClick={
    
    handleClickAsync}>Increase async</button>
    </div>
  );
}

快速多次点击按钮,count变量不能正确记录实际点击次数,有些点击被吃掉。
为了解决这个问题,使用函数方法来更新count状态,只需要将setCount(count + 1);改为setCount(count => count + 1);

猜你喜欢

转载自blog.csdn.net/LittleMoon_lyy/article/details/124518285