当前位置: 首页 > 科技观察

简化ReactHooks的5种方法

时间:2023-03-20 20:02:21 科技观察

在编写自定义Hooks时,很容易产生过于复杂的解决方案。这有时会导致不稳定的行为,创建无用的重新渲染,或者只会使其更难维护。考虑到这一点,我想分享我发现的5种有助于简化自定义Hook的方法。1、减少useState的数量当你使用hooks进行开发时,很容易使用过多的useState调用,或者将所有的状态减少到一个单一的、过于复杂的useState。提高挂钩可读性的最佳方法之一是确定useState调用的优先级。我喜欢在我写的钩子中遵循一些关于状态实现的规则。(1)优先考虑可读性我更喜欢将状态作为一个对象来读取,而不是使用简单值的多个useState命令。使用更少的useState命令也将使从钩子返回更容易和更直接地在组件中实现。虽然这是我的偏好,但代码是一件非常个人化的事情,也非常具有表现力。我在编写代码时的第一条规则是优先考虑可读性,遵循这条规则将使您的代码更易于维护,迫使您思考您编写的内容,并使其他人更容易遵循您的代码。如果这是您从本文中获得的唯一收获,那么我已经完成了我的工作。(2)评估状态对象的内容。组件并不是一开始就规划的很完美,随着组件的增长,你的useState中包含的属性也可能会变得越来越复杂。在整个开发周期中,我强烈建议评估您的useState调用的内容,以确定将状态部分拆分为其他useState调用是否有意义。您可能希望按功能或类型对状态值进行分组。通常,我喜欢根据我认为通常会一起更新的属性,或者根据状态属性的功能(例如数据和视图属性)对状态数据进行分组。2.使用你的Hook返回当我第一次开始编写自定义Hook时,很容易遵循类似于默认useStatehook的返回样式。虽然这不是一件坏事,但在函数顶部使用返回数组来返回多个状态变量可能很麻烦。想象一个钩子,除了处理数据选择的函数外,还返回2个不同的状态变量(1个用于数据状态,1个用于视图状态),用数组样式返回编写,它可能看起来像这样。functionuseBasicHook(){const[dataState,setDataState]=useState({serverData:{},selections:{}});const[viewState,setViewState]=useState({menuExpanded:false,submitFormData:{}})consttoggleMenuExpand=()=>{setViewState({menuExpanded:!viewState.menuExpanded,submitFormData:viewState.submitFormData})}return[dataState,viewState,toggleMenuExpande];}functionBasicComponent(){const[dataState,viewState,toggleMenuExpand]=useBasicHook();return

}查看这个钩子,很容易看出,如果在返回中添加额外的函数或变量,钩子的实现很快就会失控。如果你不小心打乱了数组的顺序,或者使用了不正确的名字,就会造成额外的混乱和可能的错误。我们可以通过更新钩子以返回一个对象来防止这种情况,就像这样。functionuseBasicHook(){const[dataState,setDataState]=useState({serverData:{},selections:{}});const[viewState,setViewState]=useState({menuExpanded:false,submitFormData:{}})consttoggleMenuExpand=()=>{setViewState({menuExpanded:!viewState.menuExpanded,submitFormData:viewState.submitFormData})}return{dataState:dataState,viewState:viewState,toggleMenuExpand:toggleMenuExpand};}functionBasicComponent(){conststate=useBasicHook();//或//const{dataState,viewState,toggleMenuExpand}=useBasicHook();return
}将返回值转换为对象还有其他好处,包括:如果钩子在多个组件之间共享或作为库,然后改进更新后hook版本的兼容性;当使用Hook在组件之上提供同级别的HookAPI时,对象仍然可以被解构。您可以使用钩子返回的另一件很酷的事情是在您的状态中创建基于状态的小型组件工厂函数。这提供了一种很好的方式来将组件构建器共享给实现挂钩的组件,而不会将状态暴露给该组件。3.使用合并钩子来简化setState调用在React中使用类而不是基于函数的组件进行开发在状态管理方面确实有一些开箱即用的优势,对我来说主要的优势是遗留状态与新状态的合并。ReactDocsforState提供了一个很好的例子,说明React.Component中内置的状态合并功能。虽然此功能未直接内置到挂钩中,但我们可以使用一个简单的自定义挂钩来复制此行为,该挂钩替换我们的useState调用以提供相同的行为。functionuseMergeState(initialState){const[state,setState]=useState(initialState);//异步调用setState时使用useRef改进函数。conststateRef=useRef(state);functionsetRefState(newState){stateRef.current=newState;returnsetState(newState);}functionmergeState(newState){varfinalState=newState;/***判断state数据类型是否匹配,匹配则继续合并,*如果没有匹配项,则抛出控制台警告,并用新状态覆盖。*/if(typeofstateRef.current!==typeofnewState){console.warn("useMergeStatewarning:状态数据类型不匹配,用新状态覆盖状态。");finalState=newState;}else{/***此处处理Merge*/if(typeofstateRef.current=="object"&&!Array.isArray(stateRef.current)){//现有状态为对象,继续尝试合并if(typeofnewState="object"&&!Array.isArray(newState)){finalState={...stateRef.current,...newState};}}}returnsetRefState(finalState);}return[stateRef.current,mergeState];}4.不管组件的复杂程度如何,考虑拆分Hook,我总是推荐使用customhooks;但是,在构建自定义挂钩时,将过于复杂的挂钩拆分为多个更简单的挂钩可能非常有用。在我的项目中,我喜欢根据功能拆分钩子逻辑,例如,将钩子拆分为状态的逻辑子集,例如用于数据/WebAPI交互的钩子和用于显示状态的单独钩子,可能会有好处。回想一下HooksBack部分中的示例hook,这样将它们分开可能会有所帮助。functionuseDataHook(){const[dataState,setDataState]=useState({serverData:{},selections:{}});returndataState;}functionuseDisplayHook(){const[viewState,setViewState]=useState({menuExpanded:false,submitFormData:{}})consttoggleMenuExpand=()=>{setViewState({menuExpanded:!viewState.menuExpanded,submitFormData:viewState.submitFormData})}return{viewState:viewState,toggleMenuExpand:toggleMenuExpand}functionBasicComponent(){constdata=useDataHook();constdisplay=使用显示钩子();return
}拆分示例挂钩5.评估useEffect调用以防止不必要的重新渲染useEffect挂钩非常有用,但如果使用不当会导致过度渲染。在查看自定义挂钩时,值得评估您的useEffect调用。我喜欢遵循以下经验法则:如果useEffect在同一个钩子范围内监听状态变量,那么效果不应该更新状态本身。如果您有多个useEffect语句侦听同一组变量,请考虑将它们组合在一起。虽然组合useEffects可以帮助减少重新渲染,但首先要优先考虑代码的可读性。