自从React16.8支持hook后,RN在0.59版本也支持了hook,官方的组件示例也分为class和hook两个版本。可以看出钩子是未来的趋势。正好最近RN项目重组,记录一下最近遇到的问题和思考。今天是总结课。Refactoring重构,我想没有多少人愿意做这个词。在如今的业务迭代中,重构意味着资源的消耗,并承担着未知的风险。那么为什么要重构?主要思想如下:代码郁闷,不敢删旧码。旧代码逻辑不熟悉,关系不明。随着业务的迭代,代码越来越多。模块化不明确,文档混乱。规范不统一,注释不清晰,代码混乱。文件查找困难。工程不完善,要求不统一。如果改变接口参数,需要修改很多地方。组件化不完善,公共组件不统一。hooking最重要的事情之一就是类逻辑的复用,比如://classthis.setState({count:0},()=>{//修改数据成功后处理的逻辑})//hookuseEffect(()=>{//监听计数值变化处理逻辑},[count])如果我们需要监听很多的值,是否需要写很多useEffects?这里会用到另一个概念,细粒度组件。写太多的钩子会提取一些需要处理的业务组件,每个组件只关心自己的状态。这样会大大减少父组件的业务积累和状态积累。更多的组件会涉及组件值传递。这里分为三种场景:contextwrappingsubcomponentmemo+context+reducercomponentpassingvalueref+useImperativeHandle+forwardRefexposingstatetoparentcomponentpropscomponentpassingvaluepropspassingvaluetagattribute。//props通过标签传值//exportdefaultCenterMenu(props){const{style,list}=props;//....}context包,会put所有的状态都放在父组件中,这样会导致上下文非常臃肿。//...View//constFooter=()=>{constdata=useContext(PerfectInfoContext);//...}exportdefaultFooter;用context包裹一个Reducer,然后使用memocache,我们可以触发其他功能组件的状态变化。constinitState={isLoading:true,isSignOut:false,userToken:null,routes}construducer=(prevState,action)=>{//switch}const[state,dispatch]=useReducer(reducer,initState);constauthContextProps=(调度)=>{return{signIn:async()=>{//dispatch},signOut:async()=>{//dispatch}}}constauthContextData=useMemo(authContextProps(dispatch),[]);return(//view);//其他功能组件const{signOut}=useContext(AuthContext);细粒度的,ref应该是个不错的选择,以输入框组件为例:functionFancyInput(props,ref){constinputRef=useRef(null);//暴露给父组件使用useImperativeHandle(ref,()=>({focus:()=>{inputRef.current.focus();}//其他方法也可用或状态}));return;}exportdefaultforwardRef(FancyInput);//父组件constfancyRef=useRef(null);//在useEffect和onPress中使用constonPress=()=>{fancyRef.current?.focus()}灵活的路由配置也是我们重构的一部分。RN中如何实现vue项目的路由配置?这就需要借助ReactNavigation比较遗憾的是5.x及以上版本的Stack是去掉了4.x的NavigationEvents组件。//4.x使用4.x后可以去掉}onWillBlur={payload=>console.log('willblur',payload)}onDidBlur={payload=>console.log('didblur',payload)}/>{/*Yourviewcode*/>5.x版本有点仓促,不再维护,改动比较大。核心代码分为native、stack等,可以单独使用。目前6.x版本的API大部分已经改动,不建议单独升级。//屏幕事件focus,blur,beforeRemove,stateReact.useEffect(()=>{constunsubscribe=navigation.addListener('focus',()=>{//dosomething});returnunsubscribe;},[navigation]);return至于路由配置,我们可以通过routes来控制路由的变化://路由配置exportconstroutes=[...roleRouters,{name:'Home',screen:HomeScreen,hidden:false,options:{},},...personRouters,...ordersRouters,...goodsRouters,...customerRouters,...taskRouters,...otherRouters,];{state.userToken==null?():(state.routes.map((e,i)=>{if(!e.hidden){return();}}))}{/*无论是否登录都可以访问公共路由*/}{commonRoutes.map((e,i)=>)}如果觉得import太多,navigation也提供支持,可以也可以动态导入://Dynamicimportexportconstroutes=[{name:'Home',getComponent:()=>require('@/pages/other/cmsWeb').default,options:{header:()=>{}},},];state.routes.map((e,i)=>;)导航提供辅助效果,回到顶部:import*asReactfrom'react';import{ScrollView}from'react-native';import{useScrollToTop}from'@react-navigation/native';functionAlbums(){constref=React.useRef(null);useScrollToTop(ref);//ScrollView或FlatListreturn{/*content*/>;}工程化配置除了babel和eslint配置之外就是模块化管理、路由模块化、页面模块化、api模块化工具方法、共享组件、公共钩子、公共资源、本地常量、屏幕适配方案,剩下的就是标准化和统一,这样的中小型项目基本可以举办。在钩子之前,它仍然是概念性的。这个时候在实现上可以发现一些问题,也可以和类进行对比。还是比较粗略的使用,更复杂的场景还有待处理。还要补充一点,RN中需要的本地图片返回一个id,所以我们需要集中处理。