原理:在我们每一个组件中,都需要在组件挂载的时候执行相应的任务,在组件即将卸载的时候执行相应的任务。那么在实际的开发中,很有可能这几个组件将会执行相同的逻辑,那么我们将会有大量的重复性的代码。这样的话,我们就可以将我们具有相同逻辑的代码进行抽离,抽成一个函数,在实际的业务组件中进行调用。这就是自定义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'// 子组件1const Home = () => {// 调用自定义的hookuseCustomLiftHook('Home')return <h2>Home组件</h2>}// 子组件2const About = () => {// 调用自定义的hookuseCustomLiftHook('About')return <h2>About组件</h2>}// 父组件export default function CustomHookDemo() {// 调用自定义的hookuseCustomLiftHook('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'// 引入contextimport { 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'// 引入自定义的hookimport 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的封装-可以方便的在其他的组件中直接使用
// 引入hookimport { 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]}
就是简单的函数的封装,抽取。方便再其他的业务组件中直接进行使用。不用进行二次的封装。
