让我一起聊聊Hook使用总结

自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. // hook
  8. useEffect(()=>{
  9.     // 监控count值的变更 处理逻辑
  10. },[count])

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

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

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

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

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

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

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

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

  
 
 
  1. const initState={
  2.  isLoading: true,
  3.  isSignOut: false,
  4.  userToken: null,
  5.  routes
  6. }
  7. const reducer=(prevState, action)=>{ // switch}
  8. const [state, dispatch] = useReducer(reducer, initState);
  9. const authContextProps=(dispatch)=>{
  10.     return {
  11.     signIn:async()=>{ // dispatch },
  12.     signOut:async()=>{ // dispatch}
  13.     }
  14. }
  15. const authContextData = useMemo(authContextProps(dispatch), []);
  16. return (
  17.   
  18.     // view
  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 ;
  11. }
  12. export default forwardRef(FancyInput);
  13. // 父组件
  14. const fancyRef = useRef(null);
  15. // useEffect、 onPress中使用
  16. const onPress=()=>{
  17.     fancyRef.current?.focus()
  18. }

路由

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

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

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.         {state.userToken ==  null  ? (
  19.             name='Login'
  20.             component={LoginScreen}/>) : 
  21.             (state.routes.map((e, i) => {
  22.                 if (!e.hidden) {
  23.                     return (
  24.                         
  25.                             key={i.toString()}
  26.                             name={e.name}
  27.                             params={e.params}
  28.                 component={e.screen}/>
  29.             );
  30.         }})
  31.         )}
  32.     {/* 公共路由 无论是否登录都可以访问 */}
  33.     {commonRoutes.map((e, i) => )}
  34.     

如果你觉得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.         state.routes.map(
  11.             (e, i) => ;
  12.         )
  13.     

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  {/* content */};
  8. }

工程化配置

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

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

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

网页标题:让我一起聊聊Hook使用总结
转载源于:http://www.csdahua.cn/qtweb/news36/326386.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网