自定义 React Hooks | 豆包MarsCode AI刷题

25 阅读4分钟

自定义React Hooks

1.为什么会出现hooks这个东西捏 :question:

刚看hooks 就出现了这个问题 hooks出现的意义在哪里呢

hooks能解决什么问题

React中组件有两种写法 一种类组件 一种函数组件

但是函数组件相对于类组件来说有一个小小滴缺点 就是没有state :pensive:

所以hooks就应运而生勒:grinning:

hooks是一类特殊的函数 允许在React的函数式组件中"钩入"状态,生命周期等其他React的特性

提供了一种无需类组件的方式,使得现在在函数式组件中可以使用像 this.state,this.props的概念勒:cactus:

2.那hooks都这么厉害了 为什么还要有自定义的hooks捏:grey_question:

正常的useState接受两个参数

const [state,setState]=useState('')

正常在类组件中setState会支持两个参数: 一个是更新后的state或者回调式更新的state 另一个参数是更新后的回调函数

tips:什么是回调函数:sweat:

回调 (callback) 是作为参数传递给另一个函数的函数 ,并在被调用函数执行完毕后被调用

个人的小理解: 回调函数就是先定义了functionA然后再定义了functionB

但是在使用时候先用了functionB 并且把functionA当成了参数给了functionB

useState hook并不直接支持像类组件中的setState方法那样可以接收第二个参数作为回调函数。useState hook返回的更新函数只能用于更新状态,并且不会提供回调函数的选项

所以自定义hooks就出现啦

3.来自定义useState吧:sake:

const useStatePro =(initState)=>{
    const [state,setState]=useState(initState);
    //存储一手回调函数
    const isUpdate=useRef()
    //定义一个新函数喽 (接受一个新状态和一个回调函数)
    const setStatePro =(newState,cb)=>{
        //使用setState更新状态 把回调函数储存在current里 
        //如果newState是个函数的情况下 就计算新状态 
        setState(
        prev=>{
            isUpdate.current=cb
            return typeof newState='function' ? newState(prev):newState
        }
        )
    }
    //检查一下current有无回调函数 有就直接执行
    useEffect(()=>{
        if(isUpdate.current)
            {
				return isUpdate.current()
            }
    })
    return [state,useStatePro]
}

这样就实现了useState的功能 但是多了一个在状态更新后执行回调函数的功能

4.自定义一个更新函数useUpdate

如果正常使用hooks想让组件重新渲染 一般是要更新state的

但是有的时候可能一个state掌握着好几个组件的生死大权:smiling_imp:

不能就为了一个小小的组件就让state作出无意义的更新

这时候可以想想能不能定义一个更新的hooks来优雅一些实现组件的强制更新

const Update=()=>{
    const [,setFlag]=useState()
    const update=()=>{
        //更新一手时间
        setFlag(Date.now())
    }
    return update
}

发现这个函数返回了一个函数 这个函数就是用来强制更新的

咋使用他捏:nail_care:

const Time=()=>{
    const update=useUpdate();
    return(
        {Date.now()}
   		 <div><button onCLick={update}>更新喽</button></div>
    )
}

5.自定义hooks实现redux

Redux目前来说还是常用的管理状态的工具 但是Redux需要遵守的规则和步骤有点小多:rage:

所以来制作一个属于自己的Redux

1.首先先把应用接口做好

在顶部引入Provider组件为所有的儿孙组件提供所有数据源store

import React from 'react';
import ReactDOM, {render} from 'react-dom';
import App from './components/App'
import Provider from './store/provider'
// 挂载节点
render((
    <Provider>
        <App/>
    </Provider>
), document.getElementById('app')
)
2.然后就可以开始设计store啦:happy:

首先就是数据项

//初始化数据
const initState={
 count:0;
}
//reducer 处理器
const reducer =(state,action)=>{
    const{type,payload}=action
    switch(type){
            case'ADD_COUNT':return{...state ,count:state.count+1}
            default : return state;
    }
}// 创建上下文
const Context = React.createContext()
const Provider = (props) => {
const [state, dispatch] = useReducer(reducer, initState)
return (
    <Context.Provider value={{state, dispatch}}>
        {props.children}
    </Context.Provider>
)
}
export default { Context, Provider }

在这个数据项中可以看出 initState reducer的定义和使用redux`是一模一样的

重点看下面的创建的上下文 首先通过React.createContext()创建一个空的上下文

然后定义Provider这个组件 在内部用useReducerreducer和初始化的initState传入进去

返回的statedispatch提供到Provider作为数据源

数据项聚合一下

// 聚合count、todo这些数据项
const providers = [
    Count.Provider,
    Todo.Provider
];
// 递归包裹Provider
const ProvidersComposer = (props) => (
    props.providers.reduceRight((children, Parent) => (
        return Parent({children})
    ), props.children)
)
const Provider = (props) => {
return (
    <ProvidersComposer providers={providers}>
        {props.children}
    </ProvidersComposer>
)
}
export default Provider

最后出来的组件结构: Provider > Context.Provider > Context.Provider > App 我们通过ProviderComposer进行递归包裹,把每个Provider进行一层一层的包裹 这里使用了parent({children})替代了<Parent>{children}</Parent>,这样的做法可以减少一层嵌套结构。

如何使用捏:hankey:

import React, { useContext, useEffect } from 'react';
// 引入count数据源
import CountStore from '@/store/modules/count'

const App = (props) => {
    // 通过useContext使用Count这个store的上下文
    const {state, dispatch} = useContext(CountStore.Context)
    // 每秒更新count
    useEffect(() => {
        setInterval(() => {
            dispatch({ type: 'ADD_COUNT' })
        }, 1000);
    }, [])
    
    return (
        <div className='app'>
            {JSON.stringify(state)}
        </div>
    )
}

export default App

这样就实现啦一个小型redux 感觉比正常的redux会好用一些捏