React JS中的组件组成

经过2年的React工作,我有一些经验要分享。如果您刚刚开始精通React,那么我希望本文能帮助您从1-5种五种形式到大量组件选择正确的项目开发路径,并且同时不要感到困惑。



如果您已经是专业人士,那么也许记得您的拐杖。或者,也许可以为上述问题提供更好的解决方案。



本文将集中于我对如何组织组件组成的个人见解。

从小处开始



让我们考虑一些抽象形式。我们的意思是表单中有很多字段(大约10-15个),但是为了不让眼睛睁开,我们将以一个包含4个字段的表单为例。



以下类型的多层对象到达组件的入口:



const unit = {
  name: 'unit1',
  color: 'red',
  size: {
    width: 2,
    height: 4,
  },
}


一个没有经验的开发人员(像我一样在React工作的第一个月)会在一个组件中完成所有这些工作,输入值将存储在状态中:



const Component = ({ values, onSave, onCancel }) => {
  const [ state, setState ] = useState({});

  useEffect(() => {
    setState(values);
  }, [ values, setState ]);

  return <div className="form-layout">
    <div className="form-field">
      <Input onChange={({ target: { value } }) =>
        setState((state) => ({...state, name: value }))
      }/>
    </div>
    <div className="form-field">
      <Input onChange={({ target: { value } }) =>
        setState((state) => ({...state, color: value }))
      }/>
    </div>
    <div className="size">
      <div className="form-field">
        <Input onChange={({ target: { value } }) =>
          setState((state) => ({...state, size: { width: value } }))
        }/>
      </div>
      <div className="form-field">
        <Input onChange={({ target: { value } }) =>
          setState((state) => ({...state, size: { height: value } }))
        }/>
      </div>
    </div>
    <div className="buttons">
      <Button onClick={() => onSave(state)}>Save</Button>
      <Button onClick={() => onCancel()}>Cancel</Button>
    </div>
  </div>
}


看到开发人员处理它的速度有多快,客户将提出基于此表单制作另一种表单,但没有“大小”限制。



有2个选项(并且都不正确):



  1. 您可以复制第一个组件并添加缺少的组件或删除多余的组件。通常,当他们不以自己的组件为基础并且害怕破坏其中的某些组件时,便可以执行此操作。
  2. 将其他组件设置添加到参数。


如果在3-5个表格实施后项目结束,那么开发人员会很幸运。



但是通常这仅仅是开始,并且不同模具的数量也在不断增加。



然后需要一个类似的对象,但是没有“ color”块。

然后是类似的,但带有一个新的“描述”块。

然后制作一些只读块。

然后,必须将类似的形式插入另一种形式-通常是悲伤,这有时是由悲伤引起的。



复制新表格



采用复制方法的开发人员当然会很快实现新表格。虽然会少于10。但是随后情绪会逐渐下降。



特别是在进行重新设计时。纠正“一点点”形式的块之间的凹痕,更改颜色选择组件。毕竟,不可能一everything而就,而且许多设计解决方案在实施后都必须进行修订。



重要的是要注意经常提及“相似形式”。毕竟,产品是一个,所有模具应该相似。结果,您必须处理非常乏味的常规工作,以对每种形式进行相同的返工,而且,测试人员还需要仔细检查每种形式。

通常,您会明白。请勿复制类似的组件。


归纳新形式



如果开发人员选择了第二条路径,那么他当然会骑马-您可能会认为。它只有几个组件,可以用来绘制许多形状。在整个项目中更正缩进或更改“颜色”部分-这是为了更正代码中的两行,测试人员将不得不在几个地方再次检查。



但是实际上,这条路径催生了一个很难维护的组件。



tk很难使用它。要了解很多参数,要了解每个参数的作用,您需要深入研究。



<Component
  isNameVisible={true}
  isNameDisabled={true}
  nameLabel="Model"
  nameType="input"
  isColorVisible={true}
  isColorDisabled={false}
  colorType={'dropdown'}
  isSizeVisible={true}
  isHeightVisible={true}
  isWidthDisabled={false}
/>


这也很难维护。通常,内部存在复杂的交织条件,添加新条件会破坏其他所有条件。调整组件以输出一种形式可能会破坏其余形式。

通常,您会明白。不要使组件具有许多属性。
为了解决第二种选择的问题,开发人员从什么开始呢?对。作为真正的开发人员,他们开始开发一些使设置复杂组件变得容易的东西。

例如,它们使fields参数成为可能(就像react-table中的列一样)。字段的参数在那里传递:哪个字段是可见的,哪个字段是不可编辑的,字段的名称。



组件调用变成这样:



const FIELDS = {
    name: { visible: true, disabled: true, label: 'Model', type: 'input' },
    color: { visible: true, disabled: false, type: 'dropdown' },
    size: { visible: true },
    height: { visible: true },
    width: { disabled: false },
}
<Component
  values={values}
  fields={FIELDS}
/>


结果,开发人员为自己感到骄傲。他归纳了所有字段的设置并优化了组件的内部代码:现在为每个字段调用一个函数,该函数将配置转换为相应组件的props。即使使用类型名称,也会呈现不同的组件。多一点,您将获得自己的框架。



凉?太多了。



希望它不会变成这样:



const FIELDS = {
    name: getInputConfig({ visible: true, disabled: true, label: 'Model'}),
    color: getDropDownConfig({ visible: true, disabled: false}),
    size: getBlockConfig({ visible: true }),
    height: getInputNumberConfig({ visible: true }),
    width: getInputNumberConfig({ disabled: false }),
}
<Component
  values={values}
  fields={FIELDS}
/>


通常,您会明白。不要重新发明轮子。


通过合成零部件和嵌套形状来形成新形状



让我们记住我们仍然在写什么。我们已经有一个反应库。无需发明任何新设计。使用JSX标记描述了react中的组件配置




const Form1 = ({ values }) => {
  return <FormPanel>
    <FormField disabled label=”Model”>
      <Input name="name" />
    </FormField>
    <FormField disabled label=”Color”>
      <DropDown name="color" />
    </FormField>
    <FormPanel>
      <FormField disabled label="Height">
        <Input name="height" />
      </FormField>
      <FormField disabled label="Width">
        <Input name="width" />
     </From Field>
    </FormPanelt>
  </FormPanel>
}


看来我们回到了第一个复制选项。但实际上没有。这种组合消除了前两种方法的问题。



有一组用砖组装的模板。每个砖块负责不同的事物。一些用于布局和外观,一些用于数据输入。



如果您需要在整个项目中更改缩进,那么在FormField组件中就足够了。如果您需要更改下拉列表的工作方式,则可以在DropDown组件的一处完成。



如果需要类似的形状,例如,没有“颜色”字段,则可以将通用块放在单独的砖块中,然后组装另一个形状。



我们将Size块移到一个单独的组件中:



const Size = () =>  <FormPanel>
    <FormField disabled label="Height">
      <Input name="height" />
    </FormField>
    <FormField disabled label=”Width”>
      <Input name="width" />
   </From Field>
  </FormPanel>


用各种颜色制作形状:



const Form1 = () => <FormPanel>
    <FormField disabled label="Color">
      <DropDown name="color" />
   </FormField>
    <FormField disabled label="Model">
       <Input name="name" />
    </FormField>
    <Size name="size" />
</FormPanel>


我们做出相似的形状,但是没有选择颜色:



const Form2 = () => <FormPanel>
    <FormField disabled label="Model">
       <Input name="name" />
    </FormField>
    <Size name="size" />
</FormPanel>


最重要的是,获得这种代码的人不需要处理前任的虚构配置。一切都是用JSX编写的,任何React开发人员都熟悉,每个组件都有参数提示。

通常,您会明白。使用JSX和组件组成。


关于国家的几句话



现在让我们注意状态。更确切地说,他的缺席。一旦添加状态,就关闭数据流,重用组件变得更加困难。所有砖块必须是无状态的(即无状态)。而且只有在最高层,才能将由砖块组装而成的表格连接到状态。如果表单很复杂,则将它分成几个容器并将每个部分连接到redux是有意义的。



不要懒于制作一个单独的无状态表单组件。然后,您将有机会将其用作另一种形式的一部分,或基于它创建一个有状态的形式,或用于连接到redux的容器。



当然,在砖中可能存在一些状态,用于存储与通用数据流不相关的内部状态。例如,在内部状态DropDown(下拉列表)中,可以方便地存储属性(无论是否扩展)。

通常,您会明白。将组件分为无状态和有状态。




令人惊讶的是,我经常遇到本文中描述的所有错误以及由此引起的问题。我希望您不再重复它们,然后维护您的代码将变得更加容易。



我将重复要点:



  1. 请勿复制类似的组件。使用DRY原理。
  2. 不要使组件具有很多特性和功能。每个组件应负责不同的事情(SOLID的单一职责)
  3. 将组件分为无状态和有状态。
  4. 不要重塑您的设计。在组件中使用JSX和合成。



All Articles