我们为React组件编写API,第6部分:在组件之间创建通信

为React组件编写API,第1部分:不要创建冲突的道具

编写用于React组件的API,第2部分:为行为命名,而非交互

为React组件编写API,第3部分:道具的顺序很重要

编写用于React组件的API,第4部分:当心提示!

编写用于React组件的API,第5部分:使用组合

我们为React组件编写API,第6部分:在组件之间创建通信

让我们谈谈形式。


您很可能以格式阅读了大量有关state管理的文章,但这不是此类文章之一。 相反,我想谈谈表单及其API的工作方式。


左标签


这里有很多事情,看看API


 <Form layout="label-on-left"> <Form.Field label="Name"> <TextInput type="text" placeholder="Enter your name" /> </Form.Field> <Form.Field label="Email"> <TextInput type="email" placeholder="email@domain.com" /> </Form.Field> </Form> 

让我们看一下每个组件并进行分析:


形式


所有这些都从Form组件开始, Form组件是带有附加类的基本表单元素。 它将呈现您放入其中的所有内容。


 function Form(props) { return <form className="form">{props.children}</form> } render(<Form layout="label-on-left">...</Form>) 

它还接受prop layout ,这在您空间较小时很有用。


手机上的标签


 <Form layout="label-on-top">...</Form> 

这将更改标签对齐的方式(从右到左)以及margin工作方式。


表单不控制其内部内容的宽度和margin 。 对于此表单中的输入字段,这已经是一个问题。


因此, Form组件应在下面报告layout信息。


最简单的方法是使用props传递layout ,但是表单的内容是动态的(由使用此表单的开发人员确定),我们不知道表单将是什么。


这是上下文API帮助我们的地方。


 /*    */ const LayoutContext = React.createContext() function Form(props) { /*     `Provider`       */ return ( <form className="form"> <LayoutContext.Provider value={{ layout: props.layout }} > {props.children} </LayoutContext.Provider> </form> ) } export default Form export { LayoutContext } 

现在,表单字段可以使用此上下文并获取layout


表格栏位


FormField组件(表单输入字段)会将label添加到您放置在其中的所有内容(例如,文本输入)。


 function Field(props) { return ( <div className="form-field"> <label {...props}>{props.label}</label> {props.children} </div> ) } 

除此之外,他还添加了一个用于layout的类-来自我们在Form组件中创建的上下文。


 /*   layout */ import { LayoutContext } from './form' /*           -     API (Render Prop API)         */ function Field(props) { return ( <LayoutContext.Consumer> {context => ( <div className={`form-field ${context.layout}`}> <label {...props}>{props.label}</label> {props.children} </div> )} </LayoutContext.Consumer> ) } 

React 16.8+ useContext钩子使语法更容易


 /*   layout */ import { LayoutContext } from './form' function Field(props) { /*    useContext          */ const context = useContext(LayoutContext) return ( <div className={`form-field ${context.layout}`}> <label {...props}>{props.label}</label> {props.children} </div> ) } 

如果您有兴趣,请参见以下CSS代码:


 .form-field.label-on-left { max-width: 625px; display: flex; align-items: center; /*    */ } .form-field.label-on-left label { text-align: right; width: 175px; margin-right: 25px; } .form-field.label-on-top { width: 100%; display: block; /*  flex*/ } .form-field.label-on-top label { text-align: left; /*  right */ margin-bottom: 25px; /*  margin-right */ } 

Form.Field?


我要谈的最后一个细节是组件的这种奇怪的点分语法。


由于Field (输入字段)始终与表单一起使用,因此将它们组合在一起是有意义的。


一种方法是从同一文件导出它:


 /* form.js */ import Field from './field' function Form(props) { /* ... */ } export default Form export { Field } 

现在,用户可以将它们一起导入:


 import Form, { Field } from 'components/form' render( <Form> <Field>...</Field> </Form> ) 

通过将Field附加到表单的最基本部分,我们可以进行一些小的改进。


 /* form.js */ import Field from './field' function Form(props) { /* ... */ } Form.Field = Field export default Form 

该代码之所以有效,是因为React组件是javascript对象,您可以向这些对象添加其他键。


对于用户而言,这意味着他在导入Form时会自动收到一个Field


 import Form from 'components/form' render( <Form> <Form.Field>...</Form.Field> </Form> ) 

我真的很喜欢这个API,它使FormForm.Field之间的连接显而易见。


注意:您必须将上下文移动到另一个文件,以避免循环依赖。


句点和上下文的语法组合使我们的Form组件更智能,同时保持了其对合成(复合)的可操作性。

Source: https://habr.com/ru/post/zh-CN459422/


All Articles