使用React和TypeScript编写干净代码的十大必知模式众所周知,React是一个JavaScript库,是当今最流行和行业领先的前端开发库。JavaScript是一种松散类型的语言,因此它捕获运行时。这样做的结果是JavaScript错误很晚才被发现,这可能导致严重的错误。当然React作为一个JavaScript库也继承了这个问题。整洁代码(Cleancode)[1]是一种一致的编程风格,它使代码更易于编写、阅读和维护。任何人都可以编写计算机可以理解的代码,但优秀的开发人员可以编写人类可以理解的干净代码。干净的代码是一种以读者为中心的开发风格,可以提高我们软件的质量和可维护性。编写干净的代码需要编写具有清晰和简单设计模式的代码,这使得人们易于阅读、测试和维护代码。因此,干净的代码降低了软件开发的成本。这是因为编写干净代码、消除技术债务所涉及的原则。在本文中,我们将介绍一些在使用React和TypeScript时可以使用的有用模式。为了让您的团队更轻松地保持代码健康并确定技术债务工作的优先级,请尝试Stepsize的VSCode[2]和JetBrains[3]扩展。他们帮助工程师创建技术问题,将它们添加到迭代中,并持续解决技术债务——无需离开编辑器。现在让我们来看看在使用React和Typescript时应用的10个有用模式:1.使用默认导入来导入React考虑以下代码:import*asReactfrom"react";虽然上面的代码可以工作,但如果我们不使用React中的所有内容,那么导入它们会造成混淆并且不是一个好的做法。更好的模式是像这样使用默认导出:importReact,{useContext,useState}from"react";使用这种方法,我们可以从React模块中解构我们需要的东西,而不是导入所有东西。注意:要使用此选项,我们需要按如下方式配置tsconfig.json文件:{"compilerOptions":{"esModuleInterop":true"}}在上面的代码中,我们通过将esModuleInterop设置为true[4]来启用allowSyntheticDefaultImports,这对于TypeScript支持我们的语法非常重要。2.类型声明在运行时实现之前应该考虑以下代码:importReact,{Component}from"react";constinitialState={count:1}constdefaultProps={name:"JohnDoe"}typeState=typeofinitialState;typeProps={count?:number}&typeofdefaultPropsclassCounterextendsComponent{staticdefaultProps=defaultProps;state=initialState;//...}如果我们将运行时声明分开从编译时声明开始,编译时声明在运行时声明之前,上面的代码可以更清晰,更具可读性。考虑以下代码:importReact,{Component}from"react";typeState=typeofinitialState;输入道具={count?:number}&typeofdefaultPropsconstinitialState={count:1}constdefaultProps={name:"JohnDoe"}classCounterextendsComponent{staticdefaultProps=defaultProps;状态=初始状态;//...}现在,乍一看,开发人员知道组件API长什么样,因为第一行代码清楚地表明了这一点。此外,我们将编译时声明与运行时声明分开。3.为子组件提供明确的propsTypescript通过在函数组件和类组件的react.d.ts中将它们注释为可选来反映React如何处理子组件。因此,我们需要显式地为children提供一个props类型。但是,最好用类型显式注释子属性。这在我们想要使用children进行内容投影的情况下很有用,如果我们的组件不使用它,我们可以简单地用never类型对其进行注释。考虑以下代码:importReact,{Component}from"react";//Card.tsxtypeProps={children:React.ReactNode}classCardextendsComponent{render(){const{children}=this.props;返回{children}
;这里有一些带注释的子道具类型:反应儿童|反应元素。对于原始类型,您可以使用:string|编号|布尔值。对象和数组也是有效类型。从不|空|undefined–注意:不推荐使用null和undefined。4.使用类型推断定义组件状态或者DefaultProps看下面代码:importReact,{Component}from"react";typeState={count:number};typeProps={someProps:string&DefaultProps;}typeDefaultProps={name:string}classCounterextendsComponent{staticdefaultProps:DefaultProps={name:"JohnDoe"}state={count:0}//...}虽然上面的代码有效,但我们下面的可以对其进行改进:启用TypeScript的类型系统以正确推断只读类型,例如DefaultProps和initialState。防止不小心设置state导致的开发错误:this.state={}。考虑以下代码:importReact,{Component}from"react";constinitialState=Object.freeze({count:0})constdefaultProps=Object.freeze({name:"JohnDoe"})typeState=typeofinitialState;typeProps={someProps:string}&typeofdefaultProps;classCounter扩展Component{staticreadonlydefaultProps=defaultProps;readonlystate={count:0}//...}在上面的代码中,通过冻结DefaultProps和initialState,TypeScript类型系统现在可以将它们推断为只读类型。此外,通过在类中将静态defaultProps和state标记为只读,我们消除了设置状态导致上述运行时错误的可能性。5.声明Props/State时使用类型别名(type)而不是接口(interface)虽然可以使用interface,但是为了一致性和清晰度,最好使用type,因为在某些情况下interface是行不通的。例如,在前面的示例中,我们重构了代码,以便TypeScript的类型系统通过从实现中定义状态类型来正确推断只读类型。我们不能像下面的代码那样使用这个模式的接口://workstypeState=typeofinitialState;typeProps={someProps:string}&typeofdefaultProps;//throwserrorinterfaceState=typeofinitialState;interfaceProps={someProps:字符串}&typeofdefaultProps;此外,我们不能使用联合和交集创建的类型来扩展接口,因此在这些情况下我们必须使用类型。6.不要在接口/类型中使用方法声明这确保了我们代码中模式的一致性,因为由类型/接口推断的所有成员都以相同的方式声明。此外,--strictFunctionTypes仅在比较函数而不是方法时有效。您可以从这个TS问题中得到进一步的解释。//Don'tdointerfaceCounter{start(count:number):stringreset():void}//DointerfaceCounter{start:(count:number)=>stringreset:()=>string}7.不要使用FunctionComponent或简称FC,定义一个功能组件。在使用Typescript和React时,函数组件可以有两种写法:作为普通函数,如下代码:typeProps={message:string};constGreeting=({message}:Props)=>{message}
;使用React.FC或React.FunctionComponent,像这样:importReact,{FC}from"react";typeProps={message:string};constGreeting:FC=(props)=>{props
;使用FC提供了一些优势,例如displayName、propTypes和defaultProps等静态属性的类型检查和自动完成。但它有一个破坏defaultProps和其他属性的已知问题:propTypes、contextTypes、displayName。FC还提供了一个隐式类型的子属性,它也有已知问题。此外,如前所述,组件API应该是显式的,因此隐式类型的子属性不是最佳选择。8.不要对类组件使用构造函数有了新的类属性[5]提案,就不再需要在JavaScript类中使用构造函数了。使用构造函数涉及调用super()和传递props,这引入了不必要的样板和复杂性。我们可以使用这样的类字段来编写更简洁、更易于维护的React类组件://Don'tdotypeState={count:number}typeProps={}classCounterextendsComponent{constructor(props:Props){超级(道具);this.state={count:0}}}//DotypeState={count:number}typeProps={}classCounterextendsComponent{state={count:0}}在上面的代码中,我们看到使用类属性涉及较少的样板文件,因此我们不必处理this变量。9.不要在类中使用public关键字考虑以下代码:默认情况下,所有成员在运行时都是公开的,因此无需通过显式使用public关键字来添加额外的样板文件。相反,使用以下模式:import{Component}from"react"classFriendsextendsComponent{fetchFriends(){}render(){return//jsxblob}}10.不要在组件类中使用private考虑以下代码:import{Component}from"react"classFriendsextendsComponent{privatefetchProfileByID(){}render(){return//jsxblob}}在上面的代码中,private只在编译时使fetchProfileByID方法私有,因为它只是A打字稿模拟。但是,在运行时,fetchProfileByID方法仍然是公共的。有多种方法可以使JavaScript类属性/方法私有化,使用下划线(_)变量命名约定如下:blob}}虽然这并没有真正使fetchProfileByID方法成为私有方法,但它很好地将我们的意图传达给其他开发人员,即指定的方法应被视为私有方法。其他技术包括使用Wea??kMaps、Symbols和作用域变量。但是有了新的ECMAScript类字段提案,我们可以通过使用私有字段轻松而优雅地实现这一点,如下所示:blob}}并且TypeScript在3.8及更高版本中支持私有字段的新JavaScript语法。奖励:不要使用枚举尽管枚举是JavaScript中的保留字,但使用枚举并不是标准的惯用JavaScript模式。但是,如果您使用的是C#或JAVA之类的语言,那么使用枚举可能会很诱人。然而,有更好的模式,比如像这样使用编译类型字面量://不要这样做thistype响应=成功|失败|PendingfunctionfetchData(status:Response):void=>{//somecode.}总结毫无疑问,使用Typescript会给你的代码增加很多额外的样板代码,但这样做的好处是非常值得的。为了让你的代码更干净、更好,不要忘记实施一个健壮的TODO/issue[6]过程。它将帮助您的工程团队了解技术债务、在代码库问题上进行协作以及更好地规划冲刺。本文翻译自:https://dev.to/alexomeyer/10-must-know-patterns-for-writing-clean-code-with-react-and-typescript-1m0g。参考文献[1]干净的代码:https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29。[2]步长的VS代码:https://marketplace.visualstudio.com/items?itemName=Stepsize.stepsize。[3]JetBrains:https://www.stepsize.com/r/jetbrains。[4]allowSyntheticDefaultImports:http://allowSyntheticDefaultImports。[5]类属性:https://github.com/tc39/proposal-class-fields#consensus-in-tc39。[6]待办事项/问题:https://www.stepsize.com/?utm_source=dev.to&utm_medium=referral&utm_campaign=patterns。