IT科技类资讯

让我一起聊聊Hook使用总结

时间:2010-12-5 17:23:32  作者:IT科技类资讯   来源:数据库  查看:  评论:0
内容摘要:自React 16.8支持hook以来,RN也在0.59版本支持了hook,而且官方给出的组件实例也分为了class和hook两个版本,可以遇见hook是未来的趋势。恰巧最近重构RN项目,把最近遇到的

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

今天是使用总结课。

重构

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

主要有以下思考:

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

hook化

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

// class this.setState({      count:0 },()=>{      // 修改数据成功之后 处理的逻辑 }) // hook useEffect(()=>{      // 监控count值的变更 处理逻辑 },[count]) 

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

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

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

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

// props 通过标签传值 <CenterMenu style={ style} list={ list}  /> //  export default CenterMenu(props){      const {  style, list } = props;     //. ... } 

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

<PerfectInfoContext.Provider     value={ {          serviceBill,         patientInfo,         batchList,         navigation,         ...params }}>     // ...View     <Footer  /> </PerfectInfoContext.Provider> // const Footer = () => {      const  data  =  useContext(PerfectInfoContext);     // ... } export default Footer; 

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

const initState={   isLoading: true,  isSignOut: false,  userToken: null,  routes } const reducer=(prevState, action)=>{  // switch} const [state, dispatch] = useReducer(reducer, initState); const authContextProps=(dispatch)=>{      return {      signIn:async()=>{  // dispatch },     signOut:async()=>{  // dispatch}     } } const authContextData = useMemo(authContextProps(dispatch), []); return (   <AuthContext.Provider  value={ authContextData}>     // view    </AuthContext.Provider> ); // 其他函数组件 const {  signOut } =  useContext(AuthContext); 

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

function FancyInput(props, ref) {      const inputRef = useRef(null);     // 暴露给父组件使用     useImperativeHandle(ref, () => ({      focus: () => {          inputRef.current.focus();     }     // 其他方法也可以或者state     }));     return <input ref={ inputRef} ... />; } export default forwardRef(FancyInput); // 父组件 const fancyRef = useRef(null); // useEffect、 onPress中使用 const onPress=()=>{      fancyRef.current?.focus() } <FancyInput ref={  fancyRef }></FancyInput> 

路由

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

// 4.x可使用 4.x之后被移除 <View>     <NavigationEvents         onWillFocus={ payload => console.log(will focus, payload)}         onDidFocus={ payload => console.log(did focus, payload)}         onWillBlur={ payload => console.log(will blur, payload)}         onDidBlur={ payload => console.log(did blur, payload)}     />     { /* Your view code  */} </View> 

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

// 屏幕事件focus、blur、beforeRemove、state React.useEffect(()  =>  {      const unsubscribe = navigation.addListener(focus,  ()  =>  {          // do something     });     return unsubscribe; },  [navigation]); 

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

// 路由配置 export const routes = [     ...roleRouters,     {          name: Home,         screen: HomeScreen,         hidden: false,         options: { },     },     ...personRouters,     ...ordersRouters,     ...goodsRouters,     ...customerRouters,     ...taskRouters,     ...otherRouters, ]; <NavigationContainer>     <Stack.Navigator>         { state.userToken ==  null  ? (<Stack.Screen             name=Login             component={ LoginScreen}/>) :              (state.routes.map((e, i) => {                  if (!e.hidden) {                      return (                         <Stack.Screen                             key={ i.toString()}                             name={ e.name}                             params={ e.params}                 component={ e.screen}/>             );         }})         )}     { /* 公共路由 无论是否登录都可以访问 */}     { commonRoutes.map((e, i) => <Stack.Screen  { ...e.props}  />)}     </Stack.Navigator> </NavigationContainer> 

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

// 动态导入 export const routes = [     {          name: Home,     getComponent: () =>  require(@/pages/other/cmsWeb).default,     options: {  header: () => { } },     }, ]; <NavigationContainer>     <Stack.Navigator>         state.routes.map(             (e, i) => <Stack.Screen  key={ i.toString()}  { ...e}/>;         )     </Stack.Navigator> </NavigationContainer> 

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

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

工程化配置

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

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

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

copyright © 2025 powered by 益强资讯全景  滇ICP备2023006006号-31sitemap