原理:在我们每一个组件中,都需要在组件挂载的时候执行相应的任务,在组件即将卸载的时候执行相应的任务。那么在实际的开发中,很有可能这几个组件将会执行相同的逻辑,那么我们将会有大量的重复性的代码。这样的话,我们就可以将我们具有相同逻辑的代码进行抽离,抽成一个函数,在实际的业务组件中进行调用。这就是自定义hook的原理。
自定义hook概念:在组件中,我们可能会遇到相同的业务逻辑,那么我们可以将自定义的hook进行抽离成一个单独的函数,在需要的时候进行调用,这就是自定义hook。 自定义hook的特点: 1、自定义hook实际上就是函数的抽取与封装的过程; 2、自定义hook的函数名必须使用use进行开头; 3、自定义的hook函数里面可以使用react-hooks里面相应的函数; 4、react中的hooks只能在函数式组件中使用以及在自定义hook中使用,在其他的地方无法使用。
1、劫持生命周期
import React, { useEffect } from 'react'
const Home = props => {
// 相同的事情
useEffect(() => {
console.log('Home组件创建了')
return () => {
console.log('Home组件销毁了')
}
}, [])
return <h2>Home组件</h2>
}
const About = props => {
// 相同的事情
useEffect(() => {
console.log('About组件创建了')
return () => {
console.log('About组件销毁了')
}
}, [])
return <h2>About组件</h2>
}
export default function CustomHookDemo() {
// 组件挂载和卸载完成的事件 这里是相同的事情
useEffect(() => {
console.log('CustomHookDemo组件创建了')
return () => {
console.log('CustomHookDemo组件销毁了')
}
}, [])
return (
<div>
<h2>CustomHookDemo</h2>
<Home />
<About />
</div>
)
}
存在问题:上述的重复代码太多了,每个组件都会执行相同的事情,我们需要将上述重复的代码,进行相应的抽取与封装,需要使用自定义hook来解决上述的问题,在组件中需要使用的时候,直接调用自定义hook函数。
import React, { useEffect } from 'react'
// 子组件1
const Home = () => {
// 调用自定义的hook
useCustomLiftHook('Home')
return <h2>Home组件</h2>
}
// 子组件2
const About = () => {
// 调用自定义的hook
useCustomLiftHook('About')
return <h2>About组件</h2>
}
// 父组件
export default function CustomHookDemo() {
// 调用自定义的hook
useCustomLiftHook('CustomHookDemo')
return (
<div>
<h2>CustomHookDemo</h2>
<Home />
<About />
</div>
)
}
# 自定义的hook函数 体现的的是函数的封装性思想
function useCustomLiftHook(name) {
// 使用react提供的hook 在组件挂载的时候时候执行逻辑,在组件卸载的时候,执行相应的逻辑。
useEffect(() => {
console.log(`${name}组件创建了`)
return () => {
console.log(`${name}组件销毁了`);
}
}, [])
}
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,并进行传值:
具体使用的业务组件:
使用自定义hook函数
import { useContext } from 'react'
// 引入context
import { InfoContext, ThemeContext } from '../../App'
function useCustomSharedContext() {
const info = useContext(InfoContext)
const theme = useContext(ThemeContext)
// 将context进行进行返回 提供给业务组件进行使用
return[info, theme]
}
// 将函数进行导出 给业务组件使用
export default useCustomSharedContext
具体的业务组件的使用:
import React from 'react'
// 引入自定义的hook
import useCustomSharedContext from '../hooks/customSharedContext'
// 引入context 原始代码
// import { InfoContext, ThemeContext } from "../../App";
// export default function CustomerContextHook() {
// const info = useContext(InfoContext)
// const theme = useContext(ThemeContext)
// console.log(info, theme)
// return (
// <div>
// <h2>CustomerContextHook</h2>
// </div>
// )
// }
// 使用自定义hook代码
export default function CustomerContextHook() {
// 使用数字的解构赋值
const [info, theme] = useCustomSharedContext()
console.log(info, theme)
return (
<div>
<h2>CustomerContextHook</h2>
</div>
)
}
3、获取鼠标的滚动位置
原理:鼠标每次进行滚动的时候,等待组件挂载后,我们对滚动的事件进行监听,滚动事件发生以后会触发相应的人回调函数,此时我们在回调函数中,可以实时的获取鼠标最新的位置,获取鼠标的位置后,我们可以记录鼠标最新的值,返回给需要的业务组件进行使用。同时,需要注意的是,在组件卸载的时候,需要移除监听的事件。
3.1 原始的业务组件的代码结构:
import React, { useEffect, useState } from 'react'
export default function CustomMouseScrollHook() {
// 使用useState Hook来记录鼠标滚动的位置
const [position, setPosition] = useState(0)
useEffect(() => {
// 获取鼠标滚动的位置 页面进行挂载的时候 监听滚动的事件
window.addEventListener('scroll', scrollCallback)
// 页面进行卸载的实际 移除事件的监听
return () => {
window.removeEventListener('scroll', scrollCallback)
}
})
// 滚动事件的回调函数
function scrollCallback() {
setPosition(window.scrollY)
}
return (
<div style={{ padding: '1000px 0' }}>
<h2>获取鼠标滚动位置的hook</h2>
<h2 style={{ position: 'fixed', left: 0, top: '100px' }}>页面的位置--{position}</h2>
</div>
)
}
3.2 使用hook进行相应的改造:
# 自定义的hook函数
import { useEffect, useState } from "react";
function useCustomMouseScrollPositionHook() {
// 使用useState Hook函数
const [position, setPosition] = useState(0);
// 使用useEffect来监听页面的滚动事件
useEffect(() => {
window.addEventListener('scroll', mouseScrollCallback);
return () => {
window.removeEventListener('scroll', mouseScrollCallback);
}
})
// 事件的回调函数
function mouseScrollCallback() {
// 将鼠标滚动的最新的值 添加到useState中去
setPosition(window.scrollY)
}
// 将获取结果的实时的值 传出去
return position;
}
// 将函数进行导出
export default useCustomMouseScrollPositionHook;
3.3 直接在业务组件中进行使用
import React from 'react'
// 引入封装的hook函数
import customMouseScrollHook from '../hooks/customMouseScrollPositionHook'
export default function CustomMouseScrollHook() {
// 直接执行自定义的hook函数
const position = customMouseScrollHook()
return (
<div style={{ padding: '1000px 0' }}>
<h2>获取鼠标滚动位置的hook</h2>
<h2 style={{ position: 'fixed', left: 0, top: '100px' }}>页面的位置qqqqq--{position}</h2>
</div>
)
}
在上面的案例中我们可以看出,自定义hook就是利用react现有的hook函数,对我们部分的业务模块的功能,进行相应的抽取与封装。自定义hook以后,直接在我们的业务组件中使用即可,而不再需要在我们的业务组件中再进行相应的封装。最重要的是,在我们的其它组件中也可以直接使用,而不用再进行相应的封装。
4、使用localStorage存储数据
4.1 在原有的组件中进行使用
import React, { useState, useEffect }from 'react'
export default function useCustomLoadDataHook() {
const [name, setName] = useState(() => {
const name = JSON.parse(window.localStorage.getItem('name'))
return name;
})
// 监听name的值 如果name的值发生了变化 下面这个hook就会调用
useEffect(() => {
// 数据进行序列化的操作
window.localStorage.setItem('name', JSON.stringify(name))
}, [name])
return (
<div>
<p>自定义hook-useCustomLoadDataHook</p>
<p>姓名{ name }</p>
<button onClick={ () => setName('coderweiwei') }>修改name的值</button>
</div>
)
}
4.2 自定义hook的封装-可以方便的在其他的组件中直接使用
// 引入hook
import { useState, useEffect } from 'react'
export default useCustomLocalStorageHook() {
const [name, setName] = useState(() => {
const name = JSON.parse(window.localStorage.getItem('name'))
return name;
})
// 监听name的值 如果name的值发生了变化 下面这个hook就会调用
useEffect(() => {
// 数据进行序列化的操作
window.localStorage.setItem('name', JSON.stringify(name))
}, [name])
// 将数据和方法 进行返回
return [name, setName]
}
就是简单的函数的封装,抽取。方便再其他的业务组件中直接进行使用。不用进行二次的封装。