公众号傻梦兽IT有话说欢迎大家进行一个关注,给我一点一点的小努力! 近一年的codereview的工作中,需要制作一些高可用的业务场景组件。那我的首要工作当然也是要写高可用的组件库啦。 在这一段时间的工作里面,我发现一个问题,像我们这些中小企业来讲一般都是基于antd的组件进行开发。当然react也给出了一套自己的组件封装规范。可重用的组件是什么样的? 我们以antd的modal组件为例子。按照antd的案例代码来演示。我们平时写代码的时候是怎么阅读的呢?大概是如下情况。Modalvisible{visible}titleTitleonOk{this。handleOk}onCancel{this。handleCancel}footer{〔ButtonkeybackonClick{this。handleCancel}ReturnButton,Buttonkeysubmittypeprimaryloading{loading}onClick{this。handleOk}SubmitButton,Buttonkeylinkhrefhttps:google。comtypeprimaryloading{loading}onClick{this。handleOk}SearchonGoogleButton,〕}pSomecontents。。。pSomecontents。。。pSomecontents。。。pSomecontents。。。pSomecontents。。。Modal复制代码 我们在进行抽象组件的时候,把很多的行为,使用参数的形式进行了传递,从而进行功能上的多态行为实现。这么做的问题是什么呢? 有没有发现我们在阅读代码的时候,这种写法有点变扭?我们需要去看一个title的结构的时候是不知道这个组件是长什么样子的。或者footer的这些样子我们也需要进行文档的api查阅后才知道它是用来做什么!我们只有一个children阅读量比较可观。 在这里我比较期待看到的结果是类似html的结构数据。就类似下面的代码,可读性的同时,部分的ui我们也可以进行控制render。我们追随到bootstrap的modal。buttontypebuttonclassclosedatadismissmodalarialabelClosespanariahiddentruespanbuttonh4classmodaltitleModaltitleh4pOnefinebodybuttontypebuttonclassbtnbtndefaultdatadismissmodalClosebuttonbuttontypebuttonclassbtnbtnprimarySavechangesbutton!。modalcontent!。modaldialog!。modal复制代码 这个时候我就在想,我们有没有方式把Modal变成以上的方式呢?ModalModalOpenButtonbuttonOpenModalbuttonModalOpenButtonModalContentsarialabelModallabel(forscreenreaders)ModalDismissButtonbuttonCloseModalbuttonModalDismissButtonh3Modaltitleh3SomegreatcontentsofthemodalModalContentsModal复制代码 但是,这并不是从代码量上看起来更复杂了。我们已将控制组件行为的能力赋予给了组件的使用者而不是创建者,这称为控制反转。它肯定比我们现有的antdModal组件有更多的代码,但它更简单,更灵活,适合我们未来的用例,而且不会变得更加复杂。 例如,考虑这样一种情况:我们不想只渲染表单,而是想要渲染我们喜欢的任何内容。我们的Modal支持这一点,但Modal需要接受一个新的参数。或者,如果我们希望关闭按钮显示在内容的下方,该怎么办?我们需要一个名为renderCloseBelow的特殊参数。但是对于我们的Modal,这显而易见可以轻松做到。你只需将ModalCloseButton组件移动到所需的位置即可。 更加灵活,更少的接口暴露。创建我们的第一个复合组件 我们只需要使用到Context的一点点小修改即可完成我们的操作。importasReactfromreactimportVisuallyHiddenfromreachvisuallyhiddenHeretheDialogandCircleButtonisacustomcomponentDialogisnothingbuttonsomestylesappliedonreachdialogcomponentprovidedbyreachuiimport{Dialog,CircleButton}from。libconstModalContextReact。createContext()thishelpsinidentifyingthecontextwhilevisualizingthecomponenttreeModalContext。displayNameModalContextfunctionModal(props){const〔isOpen,setIsOpen〕React。useState(false)returnModalContext。Providervalue{〔isOpen,setIsOpen〕}{。。。props}}functionModalDismissButton({children:child}){const〔,setIsOpen〕React。useContext(ModalContext)returnReact。cloneElement(child,{onClick:()setIsOpen(false),})}functionModalOpenButton({children:child}){const〔,setIsOpen〕React。useContext(ModalContext)returnReact。cloneElement(child,{onClick:()setIsOpen(true),})}functionModalContentsBase(props){const〔isOpen,setIsOpen〕React。useContext(ModalContext)return(DialogisOpen{isOpen}onDismiss{()setIsOpen(false)}{。。。props})}functionModalContents({title,children,。。。props}){return(wearemakinggenericreusablecomponentthusweallowedusercustomstylesoranyproptheywanttooverrideModalContentsBase{。。。props}ModalDismissButtonCircleButtonVisuallyHiddenCloseVisuallyHiddenspanariahiddenspanCircleButtonModalDismissButtonh3{title}h3{children}ModalContentsBase)}export{Modal,ModalDismissButton,ModalOpenButton,ModalContents}复制代码 在使用组件的过程中,我们就可以获取到一个完整的html结构树。ModalModalOpenButtonButtonLoginButtonModalOpenButtonModalContentsarialabelLoginformtitleLoginLoginFormonSubmit{register}submitButton{ButtonLoginButton}ModalContentsModal复制代码 ModalOpenButton的位置和布局你可以随意的放置,也不用去实现onClick:()setIsOpen(true),; 当然如果你想让ButtonLoing拥有onclick的事件的时候,可以参考这种思路constcallAll(。。。fns)(。。。args)fns。forEach(fnfnfn(。。。args))复制代码 只需要把以上的代码修改成functionModalOpenButton({children:child}){const〔,setIsOpen〕React。useContext(ModalContext)returnReact。cloneElement(child,{onClick:callAll(()setIsOpen(true),child。props。onClick),})}复制代码ModalOpenButtonbuttononClick{()console。log(sendingdatatofacebook;))}OpenModalbuttonModalOpenButton复制代码总结 不要匆忙地就进行组件的抽象,也不要把一切都留给参数。也许它现在是一个简单的组件,但你不知道将来需要实现哪些用例,不要认为这是时间和可维护性之间的权衡,复杂性可能会呈指数级增长。 在React发挥复合组件的优势,让你的生活更轻松。