自定义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这个组件 在内部用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'
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会好用一些捏