其实在日常的开发中,对于小型的项目,useState,useEffect,useRef已经足够了,且也无需状态管理方案。
看一下不常用的钩子有哪些吧
useMemo,缓存的钩子,useMemo接受两个参数,第一个参数是一个函数,返回值用于产生保存值。 第二个参数是一个数组,作为dep依赖项,数组里面的依赖项发生变化,重新执行第一个函数,产生新的值。一般都是用来缓存一些值,避免重复渲染子组件,或者对一些较为复杂耗时的计算的结果进行缓存,避免状态刷新后在次计算。也会与memo高阶组件组合,避免子组件重复渲染
缓存值
const newNumber = useMemo(()=>{
/** ....大量的逻辑运算 **/
return number
},[ props.number ]) // 只有 props.number 改变的时候,重新计算number的值。
也可以减少子组件的重复渲染
/* 只有当props中,list列表改变的时候,子组件才渲染 */
const ListChild = useMemo(()=> <GoodList list={ props.list } /> ,[ props.list ])
useMemo 和 useCallback 接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值,区别在于 useMemo 返回的是函数运行的结果, useCallback 返回的是函数。 返回的callback可以作为props回调函数传递给子组件。const Children = React.memo((props)=>{
useEffect(()=>{
props.getInfo('子组件')
},[])
return <div>子组件</div>
})
const Demo=({ id })=>{
const [number, setNumber] = useState(1)
/* 此时usecallback的第一参数 (sonName)=>{ console.log(sonName) }
经过处理赋值给 getInfo */
const getInfo = useCallback((sonName)=>{
console.log(sonName)
},[id])
return <div>
{/* 点击按钮触发父组件更新 ,但是子组件没有更新 */}
<button onClick={ ()=>setNumber(number+1) } >增加</button>
<Children getInfo={getInfo} />
</div>
}
useLayoutEffect: 组件更新挂载完成 -> 浏览器 dom 绘制完成 -> 执行 useEffect 回调(其实useEffect的处理时在commit刚开始就出处理了)。 useLayoutEffect 执行顺序: 组件更新挂载完成 -> 执行 useLayoutEffect 回调-> 浏览器dom绘制完成。
所以说 useLayoutEffect 代码可能会阻塞浏览器的绘制 。我们写的 effect和 useLayoutEffect,react在底层会被分别打上PassiveEffect,HookLayout,在commit阶段区分出,在什么时机执行。所以在去debug才会觉得useLayoutEffect先被触发执行,这是不对的,只是因为useLayoutEffect的回调函数的执行是同步的而已 一般useLayoutEffect在项目中是很少用到的,而且react官方也是不太推荐去使用的,除非是由特殊的需求
useReducer 接受的第一个参数是一个函数,可以认为它就是一个 reducer , reducer 的参数就是常规 reducer 里面的 state 和 action ,返回改变后的 state , useReducer 第二个参数为 state 的初始值 返回一个数组,数组的第一项就是更新之后 state 的值 ,第二个参数是派发更新的 dispatch 函数。const MyReducer = ()=>{
/* number为更新后的state值, dispatchNumbner 为当前的派发函数 */
const [ number , dispatchNumbner ] = useReducer((state,action)=>{
const { payload , name } = action
/* return的值为新的state */
switch(name){
case 'add':
return state + 1
case 'sub':
return state - 1
case 'reset':
return payload
}
return state
},0)
return <div>
当前值:{ number }
{ /* 派发更新 */ }
<button onClick={()=>dispatchNumbner({ name:'add' })} >增加</button>
<button onClick={()=>dispatchNumbner({ name:'sub' })} >减少</button>
<button onClick={()=>dispatchNumbner({ name:'reset' ,payload:666 })} >赋值</button>
{ /* 把dispatch 和 state 传递给子组件 */ }
<MyChildren dispatch={ dispatchNumbner } State={{ number }} />
</div>
}
useContext ,可以用来获取父级组件传递过来的 context 值,这个当前值就是最近的父级组件 Provider 设置的 value 值,useContext 参数一般是由 createContext 方式引入 ,也可以父级上下文 context 传递 ( 参数为 context )。const MyContext = ()=> {
const value:any = useContext(Context)
return <div> my name is { value.name }</div>
}
constContext1 = ()=>{
return <Context.Consumer>
{ (value)=> <div> my name is { value.name }</div> }
</Context.Consumer>
}
export default ()=>{
return <div>
<Context.Provider value={{ name:'alien' , age:18 }} >
<MyContext />
<Context1 />
</Context.Provider>
</div>
}
useImperativeHandle接受三个参数:
- 第一个参数ref: 接受
forWardRef传递过来的ref。 - 第二个参数
createHandle:处理函数,返回值作为暴露给父组件的ref对象。 - 第三个参数
deps:依赖项deps,依赖项更改形成新的ref对象。
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>();
const [strInt, setStrInt] = useState(() => new Set())//用来动态的控制码值定义按钮的显示
const [dataSource, setDataSource] = useState<readonly DataSourceType[]>(() => data);//表格的数据源
const actionRef = useRef()//可编辑表格的ref
useImperativeHandle(ditRef, () => {
ditRef.current.getData = async () => {
await ditRef.current.validateFields()//触发表单验证
if(editableKeys?.length>0)
{
message.error('还有未保存的数据')
return Promise.reject('还有未保存的数据')
}//只有当没有可编辑行时才保存成功
return dataSource
}//给ref加一个触发子组件的表单验证 并返回dataSource
return ditRef.current
}, [dataSource, strInt,editableKeys])//父组件通过ref触发验证并取值
//其实这里的话是处理的可编辑表格,暴露出getData的方法给父组件,父组件直接通过按钮,提交时触发我三个proTable的表单验证,在提交。用editableKeys?.lengthl来校验可编辑表格未提交的数据。
父组件
import { Button, Space } from 'antd';
import { useLocation, history } from '@umijs/max';
import BodyEditTable from './components/BodyEditable';
import EditTable from './components/editTable';
import ReturnEditTable from './components/ReturnEditTable'
import { useRef, useEffect } from 'react';
import { updateInter } from '@/services/interManage/controller'
export default () => {
const inputEditTableRef = useRef()// 输入参数表格
const bodyEditTableRef = useRef()//请求body的输入表格
const returnEditTableRef = useRef()//返回参数的可编辑表格
const { state } = useLocation()//获得上一页面传入的数据
useEffect(() => { }, [])//判断是否是编辑 是的话解析JSON数据 然后传入子组件
const submit = async () => {
const requestBody = await bodyEditTableRef.current.getData()
const inputParameters = await inputEditTableRef.current.getData()
const returnParameters = await returnEditTableRef.current.getData()
const data = JSON.parse(state)
await updateInter({ ...data, requestBodyList: requestBody, inputParametersList: inputParameters, returnParametersList: returnParameters })
history.push('/inter')
}//提交的操作
return <>
<EditTable ref={inputEditTableRef} />
<BodyEditTable ref={bodyEditTableRef} />
<ReturnEditTable ref={returnEditTableRef} />
<Space>
<Button key='test'>测试</Button>
<Button key='next' onClick={() => history.push('/inter/basic', state)}>上一步</Button>
<Button>取消</Button>
<Button key="submit" onClick={() => submit()}>保存并退出</Button>
</Space></>
}
直接通过ref绑定到子组件中的proTable,通过暴露出的getData触发对应的表单验证取值,后在提交。
这样其实当我有个可编辑表格变化时他也只会去重新渲染对应的proTable,不会引起其他两个可编辑表格的重新渲染。
当然react18也暴露了一些新的钩子。
共有 0 条评论