原理:在我们每一个组件中,都需要在组件挂载的时候执行相应的任务,在组件即将卸载的时候执行相应的任务。那么在实际的开发中,很有可能这几个组件将会执行相同的逻辑,那么我们将会有大量的重复性的代码。这样的话,我们就可以将我们具有相同逻辑的代码进行抽离,抽成一个函数,在实际的业务组件中进行调用。这就是自定义hook的原理。

自定义hook概念:在组件中,我们可能会遇到相同的业务逻辑,那么我们可以将自定义的hook进行抽离成一个单独的函数,在需要的时候进行调用,这就是自定义hook。 自定义hook的特点: 1、自定义hook实际上就是函数的抽取与封装的过程; 2、自定义hook的函数名必须使用use进行开头; 3、自定义的hook函数里面可以使用react-hooks里面相应的函数; 4、react中的hooks只能在函数式组件中使用以及在自定义hook中使用,在其他的地方无法使用。

1、劫持生命周期

  1. import React, { useEffect } from 'react'
  2. const Home = props => {
  3. // 相同的事情
  4. useEffect(() => {
  5. console.log('Home组件创建了')
  6. return () => {
  7. console.log('Home组件销毁了')
  8. }
  9. }, [])
  10. return <h2>Home组件</h2>
  11. }
  12. const About = props => {
  13. // 相同的事情
  14. useEffect(() => {
  15. console.log('About组件创建了')
  16. return () => {
  17. console.log('About组件销毁了')
  18. }
  19. }, [])
  20. return <h2>About组件</h2>
  21. }
  22. export default function CustomHookDemo() {
  23. // 组件挂载和卸载完成的事件 这里是相同的事情
  24. useEffect(() => {
  25. console.log('CustomHookDemo组件创建了')
  26. return () => {
  27. console.log('CustomHookDemo组件销毁了')
  28. }
  29. }, [])
  30. return (
  31. <div>
  32. <h2>CustomHookDemo</h2>
  33. <Home />
  34. <About />
  35. </div>
  36. )
  37. }

存在问题:上述的重复代码太多了,每个组件都会执行相同的事情,我们需要将上述重复的代码,进行相应的抽取与封装,需要使用自定义hook来解决上述的问题,在组件中需要使用的时候,直接调用自定义hook函数。

  1. import React, { useEffect } from 'react'
  2. // 子组件1
  3. const Home = () => {
  4. // 调用自定义的hook
  5. useCustomLiftHook('Home')
  6. return <h2>Home组件</h2>
  7. }
  8. // 子组件2
  9. const About = () => {
  10. // 调用自定义的hook
  11. useCustomLiftHook('About')
  12. return <h2>About组件</h2>
  13. }
  14. // 父组件
  15. export default function CustomHookDemo() {
  16. // 调用自定义的hook
  17. useCustomLiftHook('CustomHookDemo')
  18. return (
  19. <div>
  20. <h2>CustomHookDemo</h2>
  21. <Home />
  22. <About />
  23. </div>
  24. )
  25. }
  26. # 自定义的hook函数 体现的的是函数的封装性思想
  27. function useCustomLiftHook(name) {
  28. // 使用react提供的hook 在组件挂载的时候时候执行逻辑,在组件卸载的时候,执行相应的逻辑。
  29. useEffect(() => {
  30. console.log(`${name}组件创建了`)
  31. return () => {
  32. console.log(`${name}组件销毁了`);
  33. }
  34. }, [])
  35. }

2、context的共享

useContext的使用: 1、创建context,并将context进行导出; 2、使用context.provider高阶组件进行对需要接收值的组件进行包裹,并在高阶组件上通过value进行值的传递。 3、在业务组件上进行使用,先导入创建的context,然后使用useContext hook函数。将导入的context传入useContext hook中去,打印我们useContext的返回值,就得到context里面传递的值了。 存在问题: 1、每个组件都需要导入context,然后使用useContext,useContext的返回值就是我们的context的具体的值了。 2、在多个组件中,我们每次都这样使用话,就会显得比较麻烦。 解决方案: 自定义hook,将获取context的具体业务逻辑抽取到一个自定义的hook函数中,进行相应的封装,那么我们具体的业务组件 就可以直接使用我们自定义封装的hook

创建context,并进行传值:
image.png
具体使用的业务组件:
image.png
使用自定义hook函数

  1. import { useContext } from 'react'
  2. // 引入context
  3. import { InfoContext, ThemeContext } from '../../App'
  4. function useCustomSharedContext() {
  5. const info = useContext(InfoContext)
  6. const theme = useContext(ThemeContext)
  7. // 将context进行进行返回 提供给业务组件进行使用
  8. return[info, theme]
  9. }
  10. // 将函数进行导出 给业务组件使用
  11. export default useCustomSharedContext

具体的业务组件的使用:

  1. import React from 'react'
  2. // 引入自定义的hook
  3. import useCustomSharedContext from '../hooks/customSharedContext'
  4. // 引入context 原始代码
  5. // import { InfoContext, ThemeContext } from "../../App";
  6. // export default function CustomerContextHook() {
  7. // const info = useContext(InfoContext)
  8. // const theme = useContext(ThemeContext)
  9. // console.log(info, theme)
  10. // return (
  11. // <div>
  12. // <h2>CustomerContextHook</h2>
  13. // </div>
  14. // )
  15. // }
  16. // 使用自定义hook代码
  17. export default function CustomerContextHook() {
  18. // 使用数字的解构赋值
  19. const [info, theme] = useCustomSharedContext()
  20. console.log(info, theme)
  21. return (
  22. <div>
  23. <h2>CustomerContextHook</h2>
  24. </div>
  25. )
  26. }

3、获取鼠标的滚动位置

原理:鼠标每次进行滚动的时候,等待组件挂载后,我们对滚动的事件进行监听,滚动事件发生以后会触发相应的人回调函数,此时我们在回调函数中,可以实时的获取鼠标最新的位置,获取鼠标的位置后,我们可以记录鼠标最新的值,返回给需要的业务组件进行使用。同时,需要注意的是,在组件卸载的时候,需要移除监听的事件。

3.1 原始的业务组件的代码结构:

  1. import React, { useEffect, useState } from 'react'
  2. export default function CustomMouseScrollHook() {
  3. // 使用useState Hook来记录鼠标滚动的位置
  4. const [position, setPosition] = useState(0)
  5. useEffect(() => {
  6. // 获取鼠标滚动的位置 页面进行挂载的时候 监听滚动的事件
  7. window.addEventListener('scroll', scrollCallback)
  8. // 页面进行卸载的实际 移除事件的监听
  9. return () => {
  10. window.removeEventListener('scroll', scrollCallback)
  11. }
  12. })
  13. // 滚动事件的回调函数
  14. function scrollCallback() {
  15. setPosition(window.scrollY)
  16. }
  17. return (
  18. <div style={{ padding: '1000px 0' }}>
  19. <h2>获取鼠标滚动位置的hook</h2>
  20. <h2 style={{ position: 'fixed', left: 0, top: '100px' }}>页面的位置--{position}</h2>
  21. </div>
  22. )
  23. }

3.2 使用hook进行相应的改造:

  1. # 自定义的hook函数
  2. import { useEffect, useState } from "react";
  3. function useCustomMouseScrollPositionHook() {
  4. // 使用useState Hook函数
  5. const [position, setPosition] = useState(0);
  6. // 使用useEffect来监听页面的滚动事件
  7. useEffect(() => {
  8. window.addEventListener('scroll', mouseScrollCallback);
  9. return () => {
  10. window.removeEventListener('scroll', mouseScrollCallback);
  11. }
  12. })
  13. // 事件的回调函数
  14. function mouseScrollCallback() {
  15. // 将鼠标滚动的最新的值 添加到useState中去
  16. setPosition(window.scrollY)
  17. }
  18. // 将获取结果的实时的值 传出去
  19. return position;
  20. }
  21. // 将函数进行导出
  22. export default useCustomMouseScrollPositionHook;

3.3 直接在业务组件中进行使用

  1. import React from 'react'
  2. // 引入封装的hook函数
  3. import customMouseScrollHook from '../hooks/customMouseScrollPositionHook'
  4. export default function CustomMouseScrollHook() {
  5. // 直接执行自定义的hook函数
  6. const position = customMouseScrollHook()
  7. return (
  8. <div style={{ padding: '1000px 0' }}>
  9. <h2>获取鼠标滚动位置的hook</h2>
  10. <h2 style={{ position: 'fixed', left: 0, top: '100px' }}>页面的位置qqqqq--{position}</h2>
  11. </div>
  12. )
  13. }

在上面的案例中我们可以看出,自定义hook就是利用react现有的hook函数,对我们部分的业务模块的功能,进行相应的抽取与封装。自定义hook以后,直接在我们的业务组件中使用即可,而不再需要在我们的业务组件中再进行相应的封装。最重要的是,在我们的其它组件中也可以直接使用,而不用再进行相应的封装。

4、使用localStorage存储数据

4.1 在原有的组件中进行使用

  1. import React, { useState, useEffect }from 'react'
  2. export default function useCustomLoadDataHook() {
  3. const [name, setName] = useState(() => {
  4. const name = JSON.parse(window.localStorage.getItem('name'))
  5. return name;
  6. })
  7. // 监听name的值 如果name的值发生了变化 下面这个hook就会调用
  8. useEffect(() => {
  9. // 数据进行序列化的操作
  10. window.localStorage.setItem('name', JSON.stringify(name))
  11. }, [name])
  12. return (
  13. <div>
  14. <p>自定义hook-useCustomLoadDataHook</p>
  15. <p>姓名{ name }</p>
  16. <button onClick={ () => setName('coderweiwei') }>修改name的值</button>
  17. </div>
  18. )
  19. }

4.2 自定义hook的封装-可以方便的在其他的组件中直接使用

  1. // 引入hook
  2. import { useState, useEffect } from 'react'
  3. export default useCustomLocalStorageHook() {
  4. const [name, setName] = useState(() => {
  5. const name = JSON.parse(window.localStorage.getItem('name'))
  6. return name;
  7. })
  8. // 监听name的值 如果name的值发生了变化 下面这个hook就会调用
  9. useEffect(() => {
  10. // 数据进行序列化的操作
  11. window.localStorage.setItem('name', JSON.stringify(name))
  12. }, [name])
  13. // 将数据和方法 进行返回
  14. return [name, setName]
  15. }

就是简单的函数的封装,抽取。方便再其他的业务组件中直接进行使用。不用进行二次的封装。