day5--hooks

一、认识hooks

1、为什么需要hooks

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GkF5usIB-1649343862208)(img/为什么需要hooks.png)]

2.class组件存在的问题

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fYihOeL0-1649343862209)(img/class组件存在的问题.png)]

3.hook的出现

注意hook只能在函数式组件中使用
在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MkUbM87w-1649343862211)(img/hook的出现.png)]

4.计数器案例

看之前的代码,发现大量使用this

class组件的写法

import React, { PureComponent } from 'react'

export default class App extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      counter: 0
    }
  }
  render() {
    return (
      <div>
        <h2>当前计数:{this.state.counter}</h2>
        <button onClick={e => { this.setState({counter: this.state.counter + 1})}}>+1</button>
        <button onClick={e => { this.setState({counter: this.state.counter - 1})}}>-1</button>
      </div>
    )
  }
}

hooks的写法,只能在函数组件中使用

import React, { useState } from 'react'

export default function App() {
  /**
   * Hook:useState
   * > 本身是一个函数,来自react包
   * > 参数和返回值
   *  1.参数:作用是给创建出来的状态一个默认值
   *  2.返回值:数组
   *          元素一:当前state的值
   *          元素二:设置新的值时,使用的一个函数
   */
  // const arr = useState(0)
  // const state = arr[0]
  // const setState = arr[1]

  // 通常会像下面这样写
  // const [count, setCount] = [元素1, 元素2] = useState(默认值)
  const [state, setState] = useState(0)

  return (
    <div>
      <h2>当前计数:{state}</h2>
      <button onClick={() => { setState(state + 1) }}>+1</button>
      <button onClick={() => { setState(state - 1) }}>-1</button>
    </div>
  )
}

二、认识useState()

1.useState解析

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dWU5sDy9-1649343862211)(img/useState解析.png)]

2.认识useState()

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GhndxgLQ-1649343862212)(img/认识useState.png)]

3.使用useState()

import React, { useState } from 'react'

export default function MultiHookState() {
  // 多个状态多次调用useState函数
  const [count, setCount] = useState(0)
  const [age, setAge] = useState(18)

  // 复杂状态的修改
  const [friends, setFriends] = useState(['kebe', 'lilei', 'saSha'])
  let newFriends = [...friends]

  const addFriends = (value) => {
    newFriends = [...friends, value]
  }

  const [students, setStudents] = useState([
    { name: 'kebe', age: 18, height: 1.88 },
    { name: 'lihua', age: 18, height: 1.88 },
    { name: 'hanmeimei', age: 18, height: 1.88 }
  ])
  function studentAddAge(index) {
    const newStudents = [...students]
    newStudents[index].age += 1
    setStudents(newStudents)
  }
  return (
    <div>
      <h2>当前计数: {count}</h2>
      <h2>当前年龄: {age}</h2>
      <h2>好友列表:</h2>
      <ul>
        {
          friends.map((item, index) => {
            return <li key={index}>{item}</li>
          })
        }
      </ul>
      <input type="text" placeholder='添加新朋友' onInput={e => addFriends(e.target.value)} />
      <button onClick={e => { setFriends(newFriends) }}>添加朋友</button>
      <h2>学生列表</h2>
      <ul>
        {
          students.map((item, index) => {
            return (
              <li key={item.name}>
                <span>姓名:{item.name}, 年龄: {item.age}, 身高:{item.height}</span>
                <button onClick={e => studentAddAge(index)}>age+1</button>
              </li>
            )
          })
        }
      </ul>
    </div>
  )
}

4.setState可以接受一个对象,也可以接受一个cb

如果同时多次使用相同的setState(对象)那么会对这几条语句进行合并,只变化一次,

传递cb的话,里面有prev参数,可以实现多次变化,通过prev来变化

三、认识useEffect

1.认识Effect Hook

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOeKCX4K-1649343862214)(img/认识EffectHook.png)]

import React, { useEffect, useState } from 'react'

export default function App() {
  // 第一个参数,接受一个函数,并返回一个函数
  // 参数二:数组,有关性能优化,加上一个空数组的话,数据变化的时候,不会重新渲染组件
  const [counter, setCounter] = useState(0)
  useEffect(() => {
    console.log('订阅一些事件');
    return () => {
      console.log('取消订阅');
    }
  }, [])

  return (
    <div>
      <h2>当前计数:{counter}</h2>
      <button onClick={() => setCounter(counter + 1)}>+1</button>
    </div>
  )
}

2.清除副作用

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28siYM4D-1649343862214)(img/清除副作用.png)]

3.使用多个effect-hooks

  • 使用Hook的其中一个目的就是解决class中生命周期经常将很多的逻辑放在一起的问题:

    • 比如网络请求、事件监听、手动修改DOM,这些往往都会放在componentDidMount中;
  • 使用Effect Hook,我们可以将它们分离到不同的useEffect中:

    • import React, { useEffect, useState } from 'react'
      
      
      export default function App() {
        const [counter, setCounter] = useState(0)
        // 可以使用多个effct来完成多个逻辑
        // 传入空的数组,则不依赖任何值,传入counter,则依赖counter,counter变化的话,就会执行回调函数A
        useEffect(() => {
          console.log('修改DOM', counter)
        }, [counter])
        useEffect(() => {
          console.log('订阅事件')
        }, [])
        useEffect(() => {
          console.log('订阅事件')
        }, [])
      
        return (
          <div>
            <h2>当前计数:{counter}</h2>
            <button onClick={() => setCounter(counter + 1)}>+1</button>
          </div>
        )
      }
      
  • Hook 允许我们按照代码的用途分离它们, 而不是像生命周期函数那样:

    • React 将按照 effect 声明的顺序依次调用组件中的每一个 effect;

4、性能优化

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4KTrtdvi-1649343862215)(img/effct性能优化.png)]

四、useContext的使用–可以避免在子组件中使用context的value的嵌套

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7qcCuqA4-1649343862216)(img/useContext的使用.png)]

//类组件里面的context嵌套使用不方便,这样就方便太多了
import React, { useContext } from 'react'
import { UserContext, ThemeContext } from '../App'
export default function App() {
  const user = useContext(UserContext)
  const theme = useContext(ThemeContext)
  // console.log(user);
  return (
    <div>
      <h2>{user.name}, {theme.title}</h2>
    </div>
  )
}

五、useReducer的使用

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDVLmjre-1649343862217)(img/useReducer的使用.png)]

home.js

import React, { useReducer } from 'react'
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, counter: state.counter + 1 }
    case 'decrement':
      return { ...state, counter: state.counter - 1 }
    default:
      return state
  }
}
export default function Home() {
  // const [count, setCount] = useState(0)
  const [state, dispatch] = useReducer(reducer, { counter: 0 })
  return (
    <div>
      <h2>Home当前计数:{state.counter}</h2>
      <button onClick={() => dispatch({ type: 'increment' })}>+1</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
    </div>
  )
}

profile.js用了home里面的reducer函数,实现代码的复用,一般是将reducer函数抽取到单独的文件夹,这里只是为了演示

import React, { useReducer } from 'react'

// 用的是home里面的reducer实现了代码的复用
import { reducer } from './home'
export default function Home() {
  // const [count, setCount] = useState(0)
  const [state, dispatch] = useReducer(reducer, { counter: 0 })
  return (
    <div>
      <h2>Home当前计数:{state.counter}</h2>
      <button onClick={() => dispatch({ type: 'increment' })}>+1</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
    </div>
  )
}

六、useCallback的使用–性能优化

本身就是一个函数

给子组件传入useCallback包裹的函数,当父组件重新渲染,而子组件中的相关属性没有变化时,就不会重新渲染
在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-STpHC11F-1649343862218)(img/useCallback的使用.png)]

import React, { useState, useCallback, memo } from 'react'

// 浅层比较,memo函数看函数组件中的props有没有发生更新而决定要不要重新渲染
// 我们发现当HYbutton2这个组件没有被重新渲染,因为用useCallback对他进行了处理,依赖了count,就会看作increment这个属性没有发生改变,所以不会被重新渲染
// useCallback的应用场景:在将一个组件中的函数传递给子元素回调使用时,适用useCallback进行处理
const HYbutton = memo((props) => {
  console.log('HYbutton rerender');
  return <button onClick={() => props.increment()}>HYbutton+1</button>
})

export default function App() {
  // console.log('rerender');
  const [count, setCount] = useState(0)
  const [show, setShow] = useState(true)
  const increment = () => {
    // console.log('increment');
    setCount(count + 1)
  }

  // 这里是一个闭包,如果参数2是一个[]的话,就不会随着increment()变化
  const increment2 = useCallback(() => {
    console.log('increment2');
    setCount(count + 1)
  }, [count])
  return (
    <div>
      <h2>demo--{count}</h2>
      <HYbutton increment={increment}></HYbutton>
      <HYbutton increment={increment2}></HYbutton>
      <button onClick={() => setShow(!show)}>show切换</button>
    </div>
  )
}

七、useMemo的使用—性能优化

返回一个对象或者函数
在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vrOUpcUG-1649343862218)(img/useMemo的使用.png)]

// 案例一
import React, { useState, useMemo } from 'react'
function calcSum(count) {
  console.log('count + rerender');
  let total = 0
  for (let i = 1; i <= count; i++) {
    total += i
  }
  return total
}

export default function App() {
  console.log('rerender');
  const [count, setCount] = useState(3)
  const [show, setShow] = useState(true)

  // const total = calcSum(count)
  // 这样的话,除了有关count的变化会引起重新计算,其他变化不会引起重新计算
  const total = useMemo(() => {
    return calcSum(count)
  }, [count])

  return (
    <div>
      <h2>和: {total}</h2>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setShow(!show)}>切换</button>
    </div>
  )
}

// 案例二
import React, { useState, memo, useMemo } from 'react'

// memo包裹子组件,当父组件重新渲染的时候,子组件会进行一个浅比较,有变化,重新渲染,没变化,不重新渲染
// 但是每一次父组件重新渲染的时候,info都会重新定义,所以props也算是改变了,所以子组件还是跟着重新渲染
// 要达到子组件不改变的效果
// 1.可以用useState把info包裹起来
// 2.用useMemo包裹起来

const HYinfo = memo((props) => {
  console.log('子组件+rerender');
  return <h2>名字: {props.info.name} 性别: {props.info.sex}</h2>
})
export default function App() {
  console.log('父rerender');
  const [show, setShow] = useState(true)
  // const [info, setInfo] = useState({ name: 'sasha', sex: '男' })
  // const info = { name: 'sasha', sex: '男' }
  const info = useMemo(() => {
    return { name: 'sasha', sex: '男' }
  },[])
  return (
    <div>
      <HYinfo info={info}></HYinfo>
      <button onClick={() => setShow(!show)}>切换</button>
    </div>
  )
}

八、useRef

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值