自定义React hooks
自定义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 来优雅一些实现组件的强制更新
function 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 }
在这个数据项中可以看出 initStatereducer的定义和使用redux` 是一模一样的
重点看下面的创建的上下文 首先通过 React.createContext() 创建一个空的上下文
然后定义 Provider 这个组件 在内部用 useReducer把reducer和初始化的initState 传入进去
返回的 state和dispatch提供到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'
function 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 会好用一些捏