在反应

时间:2018-03-02 12:52:22

标签: javascript reactjs

我一直在想我想在我的代码库中创建一个抽象,这将消除很多样板。我的问题是关于实现子类和父类之间交互的最“正确”的方法。

我想要将很多<table />抽象为一种<Table />组件。这样做的原因是我们有很多样板可以处理可排序的标题,分页等。每次我们实现一个新表时,都会有很多复制粘贴和许多额外的测试,这些测试应该真正抽象出来

我的理想模式是:

<Table>
    <Table.Header>
        <Table.SortableHeader dataKey="id" default>ID</Table.SortableHeader>
        <Table.SortableHeader dataKey="name">Name</Table.SortableHeader>
        // ...
    </Table.Header>
    <Table.PaginatedBody pageSize=15 rowElement={ MyRowElement } />
</Table>

为了做到这一点,<Table.SortableHeader>组件需要能够设置与父<Table>组件的状态(或以其他方式交互),以便更改其排序键/顺序,以及排序键/顺序需要传递给<Table.PaginatedBody>组件。

此外,<Table.SortableHeader>组件需要知道当前的排序键/顺序,因为它们将显示不同,具体取决于排序键是否与其dataKey相同,以及排序顺序是否为{ {1}}或asc

我想到这样做的一种方法是通过在desc中传递父组件(我知道新的基于上下文的东西已经出来了,但这是一个更普遍的原则问题)。

这种方法是否会引发任何明显的问题,还有其他标准方法可以做这种事情吗?我想避免使用我传递给context组件的配置对象,然后生成结构,因为我觉得JSX是一个非常好的用于创建视图元素的DSL,而且这更加清晰。

1 个答案:

答案 0 :(得分:1)

您可以在Header组件中定义排序处理程序,并将其与排序状态一起添加到子项道具中。这种方式Table组件可以调用父排序处理程序来更新Header状态,然后将排序状态传递给cloneElement道具。

您可以使用children{ React.Children.map(this.props.children, (child) => { return React.cloneElement(child, { sort: this.state.sort, handleSort: this.handleSort }); })} 添加道具:

import {Component, NgModule, VERSION} from '@angular/core'
import { ReactiveFormsModule, FormGroup, FormBuilder, Validators } from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser'

function MustStartWithValidator (str: string) {
  return (control: SimpleControl) => {
    if (!control.value.startsWith(str)) {
      return {'mustStartWith': true};
    }

    return false;
  }
}

@Component({
  selector: 'my-app',
  template: `
    <button type="button" (click)="toggleValidation()">{{btnText()}}</button>
    <form [formGroup]="form">
      <input formControlName="myInput" type="text">
    </form>
    <div>Form isValid: {{form.valid}}</div>
    <div>Form hasError mustStartWith: {{form.hasError('mustStartWith')}}</div>
    <div>Form errors: {{form.errors | json}}</div>
    <div>Control isValid: {{form.get('myInput').valid}}</div>
    <div>Control hasError mustStartWith: {{form.get('myInput').hasError('mustStartWith')}}</div>
    <div>Control errors: {{form.get('myInput').errors | json}}</div>
  `,
})
export class App {
  shouldValidate = true;
  form: FormGroup;

  constructor(fb: FormBuilder) {
    this.form = fb.group({
      myInput: ['', MustStartWithValidator('test')]
    })
  }

  btnText () {
    if (this.shouldValidate) {
      return 'Disable validation';
    }

    return 'Enable validation';
  }

  toggleValidation () {
    this.shouldValidate = !this.shouldValidate;

    const control = this.form.get('myInput');

    if (this.shouldValidate) {
      control.setValidators(MustStartWithValidator('test'));
    } else {
      control.clearValidators();
    }

    control.updateValueAndValidity();
  }
}

@NgModule({
  imports: [ BrowserModule, ReactiveFormsModule ],
  declarations: [ App ],
  bootstrap: [ App ]
})
export class AppModule {}

完整示例代码:https://codesandbox.io/s/926nj5r6jw