💛React Hooks的使用(二) API–––Basic Hooks

创建日期:2020年3月12日
题记:本篇,不过多的去讲原理,只告诉你如何用,让你快速上手。需要知道更详细的,笔者之后再慢慢更新吧,当然,推荐你看英文版官网。中文版的有缩略,读起来也不顺畅。如果,你只想知道如何用,这篇文,足够了。
官网
中文文档

一.useState

const [state, setState] = useState(initialState);
1.initialState是初始值,只在第一次运行时候会被调用
2.每次调用setState时,都会重新渲染画面。这个过程是异步的,相当于把赋值以及re-render的动作,加入到队列中。(如果你还理解不好,第二部分,讲useEffect的时候,下面的“题外话”你可以仔细读读,按着里面的错误做法,尝试一次,就会记忆深刻了。)
3.与类组件中的 setState 方法不同,useState 不会自动合并更新对象。即,你每调用一次setState,就会重新渲染一次界面。
官方解决方案一: 你可以通过将函数更新器表单与对象扩展语法结合来复制此行为:

setState(prevState => {
  // Object.assign 也是可行的
  return {...prevState, ...updatedValues};
});

官方解决方案二:用 useReducer ,它更适合管理包含多个子值的 state(状态) 对象。具体用法待调查
个人方案: 这个例子其实不好,如果这些值都来源于网络请求,一次性更新values的所有状态,又只执行一次setState这个函数,你就会发现其中的妙处。有时间重新做个demo,加上用定时器整体赋值values,模仿网络请求

import React, {useState} from "react";
import {Box, TextField} from "@material-ui/core";

const UseStateDemo = () =>{
  const originValue = {
    k1: '',
    k2: '',
    k3: '',
    k4: '',
    k5: '',
  }
  const [values, setValues] = useState(originValue)
  const handleChange = (key: string) => (event: any) => {
    setValues({
      ...values,
      [key]: event.target.value,
    })
  }
  
  //const handleChange = event =>{ setValue(event.target.value) }
  console.log("这里运行了")
  return(
    <Box>
      <TextField
        value={values.k1}
        onChange={handleChange('k1')}
      />
     {/*......省略*/}
    </Box>
  )
}

export { UseStateDemo }

4.延迟初始化
initialState 参数是初始渲染期间使用的状态。 在随后的渲染中,它会被忽略了。 如果初始状态是高开销的计算结果,则可以改为提供函数,该函数仅在初始渲染时执行:

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

二.useEffect

useEffect(didUpdate,[依赖数组]);
1.其内部是一个函数和依赖数组,依赖数组可以省略。
2.它类似于类函数中的生命周期函数与 componentDidMount 和 componentDidUpdate。每次画面渲染完成后执行。当省略依赖数组的时候,每次渲染,该函数都会被执行,当设置依赖数组时,仅当依赖数组的值被改变时,才会被执行。
3.清理effect,当useEffect里面加了订阅或者计时器等,需要在组件卸载时,进行清理时,useEffect需要返回一个清理函数:

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Clean up the subscription
    subscription.unsubscribe();
  };
});

Demo1:我们来举个例子,说明依赖数组的作用,下面是完整的代码:

import React, {useEffect, useState} from "react";
import {Box, Button, TextField} from "@material-ui/core";

const UseEffectDemo = () =>{
  const originValue = {
    k1: '',
    k2: '',
    k3: '',
  }
  const [values, setValues] = useState(originValue)
  const [buttonDisabled, setButtonDisabled] = useState(true)
  const handleChange = (key: string) => (event: any) => {
    setValues({
      ...values,
      [key]: event.target.value,
    })
  }
  const handleClick = () => {

  }
  console.log("🔹【UseEffectDemo】运行了")
  useEffect(
    () => {
      console.log("🔴【useEffect】运行了")
      setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
    },[values]
  )
  //TODO:如何让box内部纵向布局呢?
  return(
    <Box display="flex" alignItems="flex-start">
      <TextField
        value={values.k1}
        onChange={handleChange('k1')}
      />
      <TextField
        value={values.k2}
        onChange={handleChange('k2')}
      />
      <TextField
        value={values.k3}
        onChange={handleChange('k3')}
      />
      <Button disabled={buttonDisabled} onClick={handleClick}>
        SAVE
      </Button>
    </Box>
  )
}

export { UseEffectDemo }

(1)正常运行下的输入台:

第一次启动时
每次输入一个字母后,打印出的

(2)我们删除依赖数组:

 useEffect(
   () => {
     console.log("🔴【useEffect】运行了")
     setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
   })
第一次启动时
每次输入一个字母后,打印出的

(2)我们将useEffect拆分成三个,分别依赖k1,k2,k3的值

  useEffect(
    () => {
      console.log("🔴【useEffect---k1】运行了")
      setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
    },[values.k1])
  useEffect(
    () => {
      console.log("🔴【useEffect---k2】运行了")
      setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
    },[values.k2])
  useEffect(
    () => {
      console.log("🔴【useEffect---k3】运行了")
      setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
    },[values.k3])
第一次启动时
在k1的TextFiled输入一个字母时

我们可以看到,在k1的textFiled里输入值时,仅有依赖value.k1的的useEffect运行了,其余两个同理。也就是说,当依赖数组里的值发生变化时,useEffect才会被执行。
题外话:初学者,有很多人都喜欢在TextFiled的onChange函数里去判断按钮的活性,但是,组件的setState是异步的,不会立即更新,只是将更新放在队列中。在onChage里去判断按钮活性的时候,用的并非是更新后的状态值,而是textFiled的更新前的状态值。所以,会出现bug,比如,你输入值后,又全部删除,因为判断活性用的是更新前的状态,按钮本该变为非活性的,但是,仍保持着活性状态,这是很多初学者容易犯的错误,当然,笔者当年没少被这地方坑过,特别写惯了Java这一类面向对象的开发语言后,初看react,也不大认真,网上各资料很少有强调setState的异步特性,因此很难理解这个bug是如何产生的。
另外,在官网上看到一句对setState函数的描述:

The function will receive the previous value, and return an updated value。

就刚才说的问题,有时间可以尝试一下,在TextFiled的onChange中,用setState返回的值代替state来进行判断,是否就可以了呢?但是,还是更推荐用useEffect来实现。

三.useContext

用于两个组件共享状态。
使用:https://blog.csdn.net/weixin_43606158/article/details/100750602
https://reactjs.org/docs/context.html

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Hooks是 React v16.8 的新特性,可以在不使用类组件的情况下,使用 state 以及其他的React...
    hellomyshadow阅读 13,750评论 0赞 5
  • 在学会使用React Hooks之前,可以先看一下相关原理学习React Hooks 前言 在 React 的世界...
    DC_er阅读 9,220评论 1赞 16
  • 你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗?——拥有了hooks,你再也不需...
    水落斜阳阅读 82,534评论 11赞 100
  • 前言 自react16.8发布了正式版hook用法以来,我们公司组件的写法逐渐由class向函数式组件+hook的...
    大春春阅读 4,338评论 3赞 7
  • 如果你之前对于Hooks没有了解,那么你可能需要看下概述部分。你或许也可以在一些常见的问题中找到有用的信息。 基本...
    xiaohesong阅读 21,216评论 4赞 11

友情链接更多精彩内容