useState

在函数组件中,由于没有像类组件一样有个指向实例组件的 this ,这个this里面储存类组件的状态和修改状态的方式,React为我们提供了 useState 来帮助我们保存组件的状态。

const [state, setState] = useState(initialState);

用于:用于函数组件定义state,和修改state的方法

返回一个 state,以及更新state的函数。
在初始渲染期间,返回的状态(state)与传入的第一个参数(initialState)值相同。
setState函数用于更新state。他接受一个新的 state 值并将组件的一次重新渲染加入了队列。

  1. import { useState } from 'react'
  2. export default function App() {
  3. // 定义了一个名为name的state,修改这个状态的方法叫做setName,状态的初始值为syukinmei
  4. const [name, setName] = useState('syukinmei')
  5. const [count, setCount] = useState(0)
  6. return (
  7. <>
  8. <button onClick={() => { setName('ebiebi') }}> {name} </button>
  9. {/* 写法1 */}
  10. <button onClick={() => { setCount(count + 1) }}> {count} </button>
  11. {/* 写法2 */}
  12. <button onClick={() => { setCount((prevstate) => prevstate + 1) }}> {count} </button>
  13. </>
  14. )
  15. }

跳过state更新

调用StateHook的更新函数并传入当前的state时,React将跳过子组件的渲染及effect的执行。(React使用 Object.is 比较算法来比较state)

  1. Object.is(value1, value2);
  2. // Object.is()方法确定两个值是否相同
  3. // 返回值:Boolean表示两个参数是否是相同的值

初始值为引用数据类型时

基础数据类型 直接赋值,引用数据类型 解构再赋值给它
原因:React 唯一数据源,状态只读 people状态是不能改的,需要使用setPeople创造一个新状态给他,只是新状态和旧状态名字相同做了一个覆盖。
总结:通过传入 useState 参数后返回一个带有默认状态和改变状态函数的数组。通过传入新状态给函数来改变原本的状态值。值得注意的是:useState不帮我们处理状态,相较于setState非覆盖式更新状态,useState覆盖式更新状态,需要开发者自己处理逻辑

  1. import React from 'react'
  2. export default function App() {
  3. interface People {
  4. name?: string
  5. age?: number
  6. }
  7. const [people, setPeople] = React.useState<People>({ name: 'syukinmei', age: 11 })
  8. const change = () => {
  9. // 方法用于书写修改name的逻辑
  10. // setPeople({
  11. // name:'ebibei'
  12. // })
  13. const a = people
  14. people.name = 'ebiebi'
  15. const b = people
  16. const c = { ...people, name: 'ebiebi' }
  17. console.log(Object.is(a,b)) // true !!!!!!!!!!!!!!!!!!!
  18. console.log(Object.is(b,c)) // false !!!!!!!!!!!!!!!!!!!
  19. console.log(people) // {name: "ebiebi", age: 11}
  20. // setPeople(people) // 错误写法 页面数据不更新 状态不改变 State:{age: 11, name: "ebiebi"}
  21. setPeople({...people}) // 正确写法
  22. }
  23. return (
  24. <>
  25. <button onClick={change}>setPeople</button>
  26. {people.name}
  27. {people.age}
  28. </>
  29. )
  30. }

高级写法

  1. <button onClick={() => setPeople({ ...people, age: people.age +1})}>setPeople-Age</button>

useEffect

useEffect就是一个Effect Hook,给函数组件增加了操作副作用的能力。它跟class组件中的 componentDidMount 、 componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个API,可以让函数组件实现类组件部分生命周期的功能

useEffect(callback, dependencies)

第一个参数:接受一个函数,可以来做一些副作用比如异步请求,修改外部参数等行为。

  • 该回调函数返回值(如果有):则在组件销毁或者调用函数前调用 相当于componentWillUnmount

第二个参数:是一个数组,如果数组中等值变化来才会触发执行useEffect第一个参数中的函数。

  • 数组是空数组 只有在组件初始化或销毁的时候才会触发,代替 componentDidMount 慎用(会报警告建议你删除依赖数组)
  • 数组中是state或者props 相当于componentDidUpdate
  • 不传递第二个参数,每次渲染DOM之后都会执行第一个参数的函数。
    官网案例:useEffect( ( )=>{ } )下面这个组件在 React 更新 DOM 后会设置一个页面标题

    ```typescript import React, { useState, useEffect } from ‘react’;

export default function App() { const [count, setCount] = useState(0);

// 相当于 componentDidMount 和 componentDidUpdate: 开始执行一次,组件更新执行 useEffect(() => { // 使用浏览器的 API 更新页面标题 console.log(‘执行’) document.title = You clicked ${count} times; });

return (

You clicked {count} times

); }

  1. 当你调用useEffect 时,就是告诉React在完成DOM的更改后运行你的“副作用”函数。每次渲染后都会调用副作用函数——包括第一次渲染的时候。
  2. <a name="x85aO"></a>
  3. ##### 案例:useEffect( ( )=>{ } , [ ] )
  4. 结论:组件第一次渲染会执行一次,可以获得真实DOM,监听了count的状态,状态改变触发“副作用”函数,可以用于**对某个数据的监听**<br />点击第二个button按钮不会触发“副作用”函数
  5. ```typescript
  6. import React from 'react'
  7. export default function App() {
  8. const [count, setCount] = React.useState(0);
  9. const [people, setName] = React.useState({ name: 'syukinmei', age: 18 })
  10. // 相当于 componentDidMount 和 componentDidUpdate:
  11. React.useEffect(() => {
  12. console.log('执行') // 第一次渲染会执行
  13. console.log(document.querySelector('button')) // 可以获得真实DOM
  14. }, [count]);
  15. return (
  16. <div>
  17. <button onClick={() => setCount(count + 1)}> You clicked {count} times </button>
  18. <button onClick={() => setName({ ...people, age: people.age + 1 })}> {people.name} is {people.age} years old </button>
  19. </div>
  20. );
  21. }

案例:React.useEffect( () => { return ()=>{ } } );
  1. import React from 'react'
  2. export default function App() {
  3. const [Flag, setFlag] = React.useState(true);
  4. // 相当于 componentDidMount 和 componentDidUpdate:
  5. React.useEffect(() => {
  6. console.log('App执行') // 第一次渲染会执行
  7. console.log('App',document.querySelector('h1')) // 可以获得真实DOM
  8. return ()=>{
  9. console.log('App组件被销毁了')
  10. }
  11. });
  12. return (
  13. <div onClick={() => { setFlag(!Flag) }}>
  14. <h1 >title</h1>
  15. { Flag && <Child />}
  16. </div>
  17. );
  18. }
  19. function Child() {
  20. React.useEffect(() => {
  21. console.log('Child执行') // 第一次渲染会执行
  22. console.log('Child', document.querySelector('h1')) // 获得的是App组件中的h1标签???
  23. return ()=>{
  24. console.log('Child组件被销毁了')
  25. }
  26. });
  27. return (
  28. <div>
  29. <h1>Child组件</h1>
  30. </div>
  31. )
  32. }
  • 执行结果:

image.png

  • 正常写法:当Child组件销毁 执行Child组件中的“副作用”函数的返回值 ```typescript import React from ‘react’ export default function App() { const [Flag, setFlag] = React.useState(true); return (
    1. <div onClick={() => { setFlag(!Flag) }}>
    2. <h1 >title</h1>
    3. { Flag && <Child />}
    4. </div>
    ); }

function Child() { React.useEffect(() => { return ()=>{ console.log(‘Child组件被销毁了’) } }); return (

Child组件

)

  1. <a name="ZtRXl"></a>
  2. ##### 案例:使用副作用函数实现 组件销毁阶段消除计时器,消除绑定在window或Document身上的事件,删除第三方库实例
  3. ```typescript
  4. import React from 'react'
  5. import Swiper from 'swiper'
  6. export default function App() {
  7. const [Flag, setFlag] = React.useState(true);
  8. return (
  9. <div onClick={() => { setFlag(!Flag) }}>
  10. <h1 >title</h1>
  11. { Flag && <Child />}
  12. </div>
  13. );
  14. }
  15. function Child() {
  16. let timer: any = null // 用于销毁计时器
  17. let swiper: any = null
  18. console.log(timer)
  19. React.useEffect(() => {
  20. timer = setInterval(() => {
  21. console.log('计时器')
  22. }, 1000)
  23. window.onresize = function () {
  24. console.log('可视窗口尺寸改变了')
  25. }
  26. swiper = new Swiper('container')
  27. return () => {
  28. console.log('Child组件被销毁了')
  29. // 删除计时器
  30. clearInterval(timer)
  31. // 删除绑定在window/document身上的事件
  32. window.onresize = null
  33. // 清除第三方库实例化
  34. swiper = null
  35. }
  36. });
  37. return (
  38. <div>
  39. <h1>Child组件</h1>
  40. <div className="container"></div>
  41. </div>
  42. )
  43. }