让我一起聊聊Hook使用总结

开发 前端
自React 16.8支持hook以来,RN也在0.59版本支持了hook,而且官方给出的组件实例也分为了class和hook两个版本,可以遇见hook是未来的趋势。恰巧最近重构RN项目,把最近遇到的问题和思考记录一下。

[[423277]]

自React 16.8支持hook以来,RN也在0.59版本支持了hook,而且官方给出的组件实例也分为了class和hook两个版本,可以遇见hook是未来的趋势。恰巧最近重构RN项目,把最近遇到的问题和思考记录一下。

今天是总结课。

重构

重构,我想这个词可能没有多少人愿意做,在业务迭代频繁的今天,重构意味着资源的消耗,而且承担着未知的风险,那么为什么还要重构?

主要有以下思考:

  • 代码沉郁,不敢删除旧代码,旧有的代码逻辑不熟,关联关系不清,随着业务的迭代,代码越来越多。
  • 模块化不清晰,文件混乱。
  • 规范不统一,注释不明确,代码杂乱。文件查找困难。
  • 工程化不完善,请求不统一,如遇到接口参数变更,需要修改多处。
  • 组件化不完善,公共组件不统一。

hook化

hook化一个很重要的就是class逻辑的复用,比如:

  1. // class 
  2. this.setState({ 
  3.     count:0 
  4. },()=>{ 
  5.     // 修改数据成功之后 处理的逻辑 
  6. }) 
  7.  
  8. // hook 
  9. useEffect(()=>{ 
  10.     // 监控count值的变更 处理逻辑 
  11. },[count]) 

如果我们需要监控的值很多,是否需要写很多useEffect呢?这里会用到另一个概念,细粒度组件。写hook写多了,会把一些需要处理的业务组件都抽离出来,每个组件只管自己的状态。这样就会极大减少了父组件的业务堆积和state堆积。

组件多了就会涉及到组件传值,这里有三种场景:

  • context包裹子组件
  • memo + context + reducer 组件传值
  • ref + useImperativeHandle + forwardRef 暴露状态给父组件
  • props 组件传值

props传值,通过标签属性传递。

  1. // props 通过标签传值 
  2. <CenterMenu style={style} list={list}  /> 
  3.  
  4. //  
  5. export default CenterMenu(props){ 
  6.     const { style, list } = props; 
  7.     //. ... 

context包裹的话,就会把所有状态都放在了父组件,就会造成context很臃肿。

  1. <PerfectInfoContext.Provider 
  2.     value={{ 
  3.         serviceBill, 
  4.         patientInfo, 
  5.         batchList, 
  6.         navigation, 
  7.         ...params 
  8. }}> 
  9.     // ...View 
  10.     <Footer  /> 
  11. </PerfectInfoContext.Provider> 
  12.  
  13. // 
  14. const Footer = () => { 
  15.     const  data  =  useContext(PerfectInfoContext); 
  16.     // ... 
  17. export default Footer; 

用context包裹一个Reducer,再用memo缓存,我们就可以在其他函数组件中去触发状态变更。

  1. const initState={ 
  2.  isLoading: true
  3.  isSignOut: false
  4.  userToken: null
  5.  routes 
  6. const reducer=(prevState, action)=>{ // switch} 
  7. const [state, dispatch] = useReducer(reducer, initState); 
  8.  
  9. const authContextProps=(dispatch)=>{ 
  10.     return { 
  11.     signIn:async()=>{ // dispatch }, 
  12.     signOut:async()=>{ // dispatch} 
  13.     } 
  14. const authContextData = useMemo(authContextProps(dispatch), []); 
  15. return ( 
  16.   <AuthContext.Provider  value={authContextData}> 
  17.     // view 
  18.    </AuthContext.Provider> 
  19. ); 
  20.  
  21. // 其他函数组件 
  22. const { signOut } =  useContext(AuthContext); 

细粒度的话,ref应该是一个很好的选择,拿输入框组件举例:

  1. function FancyInput(props, ref) { 
  2.     const inputRef = useRef(null); 
  3.     // 暴露给父组件使用 
  4.     useImperativeHandle(ref, () => ({ 
  5.     focus: () => { 
  6.         inputRef.current.focus(); 
  7.     } 
  8.     // 其他方法也可以或者state 
  9.     })); 
  10.     return <input ref={inputRef} ... />; 
  11. export default forwardRef(FancyInput); 
  12.  
  13. // 父组件 
  14. const fancyRef = useRef(null); 
  15. // useEffect、 onPress中使用 
  16. const onPress=()=>{ 
  17.     fancyRef.current?.focus() 
  18.  
  19. <FancyInput ref={ fancyRef }></FancyInput> 

路由

灵活的路由配置也是我们重构要考虑的一部分,怎么在RN中实现vue项目的路由配置呢?这需要借助React Navigation 5.x以上版本的Stack。比较可惜的是4.x的 NavigationEvents组件被移除。

  1. // 4.x可使用 4.x之后被移除 
  2. <View
  3.     <NavigationEvents 
  4.         onWillFocus={payload => console.log('will focus', payload)} 
  5.         onDidFocus={payload => console.log('did focus', payload)} 
  6.         onWillBlur={payload => console.log('will blur', payload)} 
  7.         onDidBlur={payload => console.log('did blur', payload)} 
  8.     /> 
  9.     {/* Your view code  */} 
  10. </View

 

5.x版本有点仓促,已不在维护,变更较大,核心代码分为native、stack等,可以单独使用。现在的版本6.x大部分api都做了变更,不推荐单独升级。

  1. // 屏幕事件focus、blur、beforeRemove、state 
  2. React.useEffect(()  =>  { 
  3.     const unsubscribe = navigation.addListener('focus',  ()  =>  { 
  4.         // do something 
  5.     }); 
  6.     return unsubscribe; 
  7. },  [navigation]); 

回到路由配置上,我们可以通过routes来控制路由变化:

  1. // 路由配置 
  2. export const routes = [ 
  3.     ...roleRouters, 
  4.     { 
  5.         name'Home'
  6.         screen: HomeScreen, 
  7.         hidden: false
  8.         options: {}, 
  9.     }, 
  10.     ...personRouters, 
  11.     ...ordersRouters, 
  12.     ...goodsRouters, 
  13.     ...customerRouters, 
  14.     ...taskRouters, 
  15.     ...otherRouters, 
  16. ]; 
  17.  
  18. <NavigationContainer> 
  19.     <Stack.Navigator> 
  20.         {state.userToken ==  null  ? (<Stack.Screen 
  21.             name='Login' 
  22.             component={LoginScreen}/>) :  
  23.             (state.routes.map((e, i) => { 
  24.                 if (!e.hidden) { 
  25.                     return ( 
  26.                         <Stack.Screen 
  27.                             key={i.toString()} 
  28.                             name={e.name
  29.                             params={e.params} 
  30.                 component={e.screen}/> 
  31.             ); 
  32.         }}) 
  33.         )} 
  34.  
  35.     {/* 公共路由 无论是否登录都可以访问 */} 
  36.     {commonRoutes.map((e, i) => <Stack.Screen  {...e.props}  />)} 
  37.     </Stack.Navigator> 
  38. </NavigationContainer> 

 

 

如果你觉得import导入太多的话,navigation也提供了支持,你也可以动态导入:

  1. // 动态导入 
  2. export const routes = [ 
  3.     { 
  4.         name'Home'
  5.     getComponent: () =>  require('@/pages/other/cmsWeb').default
  6.     options: { header: () => {} }, 
  7.     }, 
  8. ]; 
  9.  
  10. <NavigationContainer> 
  11.     <Stack.Navigator> 
  12.         state.routes.map( 
  13.             (e, i) => <Stack.Screen  key={i.toString()}  {...e}/>; 
  14.         ) 
  15.     </Stack.Navigator> 
  16. </NavigationContainer> 

 

 

navigation提供了一个辅助效果,回到顶部:

  1. import  *  as  React  from  'react'
  2. import  {  ScrollView  }  from  'react-native'
  3. import  { useScrollToTop }  from  '@react-navigation/native'
  4. function  Albums()  { 
  5.     const ref =  React.useRef(null); 
  6.     useScrollToTop(ref); // ScrollView或者FlatList 
  7.     return  <ScrollView ref={ref}>{/* content */}</ScrollView>; 

工程化配置

除了babel、eslint配置外,就是模块化的管理,路由模块化、页面模块化、api模块化。工具方法、共用组件、公共hook、公共资源、本地常量,以及屏幕适配方案,剩下就是规范统一,这样一个小中项目基本就可以hold住了。

hook之前都停留着概念上,这次的落地能发现一些问题,也能跟贴切与class对比,目前还是粗浅使用,更加复杂的场景还待处理。

补充一点就是,在RN中require的本地图片返回的是一个id,那么我们有集中处理必要了。

 

责任编辑:武晓燕 来源: 惊天码盗
相关推荐

2021-04-12 18:03:39

Nginx架构负载

2021-07-02 20:46:06

Go接口动态

2022-06-27 08:00:49

hook工具库函数

2024-07-18 08:54:49

2021-07-12 11:35:13

Go协程Goroutine

2023-08-04 08:20:56

DockerfileDocker工具

2023-08-10 08:28:46

网络编程通信

2022-05-24 08:21:16

数据安全API

2023-09-10 21:42:31

2023-09-21 08:16:56

JDK 21向量计算计算

2023-06-30 08:18:51

敏捷开发模式

2024-02-20 21:34:16

循环GolangGo

2021-08-27 07:06:10

IOJava抽象

2022-12-07 09:01:14

布局容器VStack​

2018-08-24 15:24:10

AndroidiOS苹果

2024-04-07 00:00:10

Rust枚举C代码

2023-03-26 23:47:32

Go内存模型

2022-02-23 08:41:58

NATIPv4IPv6

2022-10-08 00:00:05

SQL机制结构

2023-03-28 10:25:59

静默GlobalFlag进程
点赞
收藏

51CTO技术栈公众号