React Hooks进阶

156 阅读5分钟
  • useState 回调函数形式的参数
  • useEffect 清理副作用
  • useRef操作DOM
  • useContext组件通讯

useEffect发送请求

目的

能够在函数组件中通过useEffect发送ajax请求

格式

// 正确使用
useEffect(() => {
  // 定义一个函数
  async function fetchMyAPI() {
    let url = 'http://something/' + productId
    let config = {}
    const response = await myFetch(url)
  }
  // 调用它
  fetchMyAPI()
}, [productId])

注意:effect 只能是一个同步函数,不能使用 async

// 错误演示:

// 不要给 effect 添加 async
useEffect(async () => {
    const res = awiat xxx()
    return ()=> {
        
    }
}, [])

useEffect-副作用函数的返回值-清理副作用

副作用函数的返回值

格式

useEffect(() => {
  // 副作用函数的内容
​
  return 副作用函数的返回值
}, [])

副作用函数的返回值是可选的,可省略。也可以返回一个清理函数,用来清理副作用

useEffect(() => {
  // 副作用函数的内容
​
  return ()=>{ /* 做清理工作*/ } // 清理函数
}, [])

清理函数的执行时机:

  • 清理函数在组件卸载时以及下一次副作用函数调用之前执行

示例:执行时机

import React, { useEffect, useState } from 'react'
import ReactDom from 'react-dom'function Com1 () {
  useEffect(() => {
    console.log('子组件的副作用函数')
    
    return ()=>{
      console.log('子组件的副作用函数的清理函数')
    }
  }, [])
  return <div>子组件</div>
}
export default function App () {
  const [isShow, setIsShow] = useState(true)
  const [count, setCcount] = useState(0)
  
  useEffect(() => {
    console.log('父组件的副作用函数的内容')
​
    return () => {
      
      console.log('父组件的副作用函数的 清理函数 的内容')
    }
  }, [count])
  
  return (
    <div>
      {isShow && <Com1 />}
      <button onClick={() => setIsShow(!isShow)}>删除|创建子组件</button>
        <button onClick={() => setCcount(count+1)}>点击+1, {count}</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
​

示例-模拟componentWillUnmount

useEffect(()=>{
  return ()=>{
    // 做清理工作
  }
}, [])

小结

情况1:不带第二个参数。执行时机:初始执行,每次更新之后都要执行。

模拟 componentDidmount + componentDidUpdate

useEffect(() => {
  // 副作用函数的内容
})

情况2:带第二个参数,参数是空数组。执行时机:只执行第一次。

模拟 componentDidMount

useEffect(() => {
  // 副作用函数的内容
}, [])

使用场景:1 事件绑定 2 发送请求获取数据 等。

情况3:带第二个参数(数组格式),并指定了依赖项。执行时机:(1)初始执行一次 (2)依赖项的值变化了,执行一次

useEffect(() => {
  // 副作用函数的内容
}, [依赖项1,依赖项2,....])

这里的依赖项就是组件中定义的状态。

情况4:依赖项为空,没有具体的副作用函数,有副作用函数的清理函数

模拟:componentWillUnMount

useEffect(() => {

      return () => {
        // 副作用函数的清理函数,模拟 componentWillUnMount
      }

    }, [])

seEffect-清理副作用-案例

目标

掌握清理组件中定义的事件

问题导入

删除子组件之后,事件还有么?

import React, { useEffect, useState } from 'react'
import ReactDom from 'react-dom'function Com1 () {
  useEffect(() => {
    window.addEventListener('mousemove', (e) => {
      console.log(e.pageX, e.pageY)
    })
  }, [])
  return <div>子组件</div>
}
export default function App () {
  const [isShow, setIsShow] = useState(true)
  return (
    <div>
      {isShow && <Com1 />}
      <button onClick={() => setIsShow(!isShow)}>切换</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
​

子组件被删除了,但是,事件处理还在,所以要清理副作用。

解决

import React, { useEffect, useState } from 'react'
import ReactDom from 'react-dom'function Com1 () {
  useEffect(() => {
    const fn = (e) => {
      console.log(e.pageX, e.pageY)
    }
+    window.addEventListener('mousemove', fn)
​
+    return () => {
      window.removeEventListener('mousemove', fn)
      console.log('组件删除了')
    }
  }, [])
  return <div>子组件</div>
}
export default function App () {
  const [isShow, setIsShow] = useState(true)
  return (
    <div>
      {isShow && <Com1 />}
      <button onClick={() => setIsShow(!isShow)}>切换</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
​

案例-鼠标跟随

目标

能够实现案例,让图片跟随鼠标移动

思路

  • 通过useState提供状态
  • 通过useEffect给document注册鼠标移动事件
  • 在组件销毁的时候清理副作用
import { useEffect, useState } from 'react'
import img from './1.gif'
export default function Move() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  })
  useEffect(() => {
    const move = (e) => {
      console.log('移动')
​
      setPosition({
        x: e.pageX,
        y: e.pageY
      })
    }
    document.addEventListener('mousemove', move)
    console.log('注册事件')
    return function () {
      document.removeEventListener('mousemove', move)
    }
  }, [])
  return (
    <div>
      <img
        src={img}
        style={{
          position: 'absolute',
          top: position.y + 1,
          left: position.x + 1
        }}
        alt=""
      />
    </div>
  )
}

自定义hooks-实操

定义

除了使用内置的 Hooks 之外,还可以创建自己的 Hooks(自定义 Hooks)。

自定义 Hooks 针对不同组件实现不同状态逻辑复用

  • 自定义 Hooks 是一个函数,约定函数名称必须以 use 开头,React 就是通过函数名称是否以 use 开头来判断是不是 Hooks
  • Hooks 只能在函数组件中或其他自定义 Hooks 中使用,否则,会报错!
  • 自定义 Hooks 用来提取组件的状态逻辑,根据不同功能可以有不同的参数和返回值(就像使用普通函数一样)

使用场景

将组件状态逻辑提取到可重用的函数(自定义 Hooks)中,实现状态逻辑复用。

核心代码

// museMouse.js
import { useEffect, useState } from 'react'
export default function useMouse() {
  // 逻辑处理
  const [position, setPosition] = useState({
    x: 0,
    y: 0,
  })
​
  useEffect(() => {
    const move = (e) => {
      setPosition({
        x: e.pageX,
        y: e.pageY,
      })
    }
    document.addEventListener('mousemove', move)
    return () => {
      document.removeEventListener('mousemove', move)
    }
  }, [])
  
  // 返回状态
  return position
}

小结

  1. 自定义hooks是一个_
  2. 自定义hooks的名字必须以_开头
  3. 自定义hooks只能在_和_中使用

useRef-操作DOM

目标

能够使用useRef操作DOM

内容

使用场景:在 React 中进行 DOM 操作时,用来获取 DOM

作用:返回一个带有 current 属性的可变对象,通过该对象就可以进行 DOM 操作了。

const inputRef = useRef(null)

解释:

  • 参数:在获取 DOM 时,一般都设置为 null
  • 返回值:包含 current 属性的对象。
  • 注意:只要在 React 中进行 DOM 操作,都可以通过 useRef Hook 来获取 DOM(比如,获取 DOM 的宽高等)。
  • 注意:useRef不仅仅可以用于操作DOM,还可以操作组件

核心代码:

// 1. 导入 useRef
import React, { useRef } from 'react'
import ReactDom from 'react-dom'class Com1 extends React.Component {
  state = {
    a: 100
  }
​
  render () {
    return <div>com1, {this.state.a}</div>
  }
}
​
export default function App () {
  // 2. 调用useRef(初值),得到引用对象
  // 3. 把引用对象设置ref 给任意的组件/元素
  // 4. 通过引用对象.current 获取 组件/元素
  const refTxt = useRef(null)
  const refCom1 = useRef(null)
  console.log(refTxt)
​
  const click = () => {
    console.log(refTxt, refCom1)
    console.log(refCom1.current.state.a)
    // console.log(refTxt.current.value)
    // refTxt.current.style.backgroundColor = 'red'
  }
  return (
    <div>
      useRef, <input ref={refTxt} />{' '}
      <button onClick={click}>点击,获取input中的值</button>
      <br />
      <Com1 ref={refCom1} />
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
​

\