Definitelytyped: Benötige Beispiele für React.Children.map() und React.cloneElement()

Erstellt am 26. Okt. 2016  ·  8Kommentare  ·  Quelle: DefinitelyTyped/DefinitelyTyped

React.Children.map() fehlt ein gutes Beispiel , ich verstehe nicht, wie man es eingibt:

/*
<CheckboxGroup ...>
  <Checkbox ... />
  <Checkbox ... />
  <Checkbox ... />
</CheckboxGroup>
*/

// Child
class Checkbox extends React.Component<CheckboxProps, void> {
  // ...
}

// Parent
class CheckboxGroup extends React.Component<CheckboxGroupProps, void> {
  // ...

  render() {
    const checkboxes = React.Children.map(this.props.children, checkbox =>
      React.cloneElement(checkbox, {
        name: this.props.name,
        checked: this.props.checkedValues.includes(checkbox.props.value),
        onChange: this.handleCheckboxChange.bind(this)
      })
    );

    return (
      <div>
        {checkboxes}
      </div>
    );
  }
}

(sehr häufiges Beispiel, siehe http://stackoverflow.com/a/32371612/990356)

  • Wie tippe ich this.props.children ?
  • Wie tippe ich React.cloneElement() ?
  • Wie tippe ich React.Children.map() ?

Meistens erhalte ich den folgenden Fehler: Type 'string' is not assignable to type 'ReactElement<any>' , siehe #8131

Hilfreichster Kommentar

Frage:

const checkboxes = React.Children.map(this.props.children, checkbox =>
  React.cloneElement(checkbox, {
    name: this.props.name,
    checked: this.props.checkedValues.includes(checkbox.props.value),
    onChange: this.handleCheckboxChange.bind(this)
  })
);

Lösung:

const checkboxes = React.Children.map(props.children, (checkbox: React.ReactElement<CheckboxPropsInternal>) =>
  React.cloneElement(checkbox, {
    checked: props.checkedValues.includes(checkbox.props.value),
    onChange: handleCheckboxChange
  })
);

Alle 8 Kommentare

Bitte senden Sie einen Pull-Request. Ich werde es überprüfen.

@vvakame Ich kann nicht, ich verstehe nicht, wie es funktionieren soll

ich auch. Bitte führen Sie Gespräche mit Definitionsautoren.

Sie müssen checkbox explizit annotieren oder bestätigen, da es keine Garantie dafür gibt, dass this.props.children keine Nicht-ReactElement-Werte enthält. zum Beispiel:

React.Children.map(this.props.children, (checkbox: React.ReactElement<CheckboxProps>) => 
    React.cloneElement(checkbox) // should be ok
);

@vsiao vielen Dank, es funktioniert!

Frage:

const checkboxes = React.Children.map(this.props.children, checkbox =>
  React.cloneElement(checkbox, {
    name: this.props.name,
    checked: this.props.checkedValues.includes(checkbox.props.value),
    onChange: this.handleCheckboxChange.bind(this)
  })
);

Lösung:

const checkboxes = React.Children.map(props.children, (checkbox: React.ReactElement<CheckboxPropsInternal>) =>
  React.cloneElement(checkbox, {
    checked: props.checkedValues.includes(checkbox.props.value),
    onChange: handleCheckboxChange
  })
);

@tkrotoff Sie könnten auch dies tun:

    React.Children.map(this.props.children, (child: number) => {
      return child + 1
    })

Dies und die vorgeschlagene Lösung nutzen nur die Tatsache, dass React.Children.map nicht stark typisiert ist. Sie hätten auch any anstelle des Typs Checkbox verwenden können, da es keine Garantie dafür gibt, dass dies der Typ der Kinder ist. Dies bedeutet, dass wahrscheinlich Laufzeitfehler auftreten, wenn jemand die Komponente falsch verwendet, indem er eine Zeichenfolge übergibt.

Wie wäre es damit:

class Slot extends React.PureComponent {
  render () {
    return this.props.children
  }
}

const isReactElement = (obj: {}): obj is React.ReactElement<{}> => {
  return obj.hasOwnProperty('type')
}

const isSlot = (obj: {}): obj is Slot => {
  return isReactElement(obj) && obj.type === Slot
}

const getSlots = (children: React.ReactNode) => React.Children.map(children, (child: React.ReactChild): JSX.Element | null => {
  if (isReactElement(child) && isSlot(child)) {
    return child
  }

  return null
})

class ComponentWithSlots extends React.PureComponent {
  render () {
    const [header, footer] = getSlots(this.props.children)

    return (
      <div>
        {header}
        <div>
          <h1>Welcome</h1>
          <p>This is my lovely component with slots</p>
        </div>
        {footer}
      </div>
    )
  }
}

class MyComponent extends React.PureComponent {
  render () {
    return (
      <ComponentWithSlots>
        <Slot>My Header!</Slot>
        <Slot>
          <div>github: @variousauthors</div>
        </Slot>
      </ComponentWithSlots>
    )
  }
}

Was rendert:

My Header!
Welcome
This is my lovely component with slots

github: <strong i="17">@variousauthors</strong>

Dieser Ansatz nutzt die Typinformationen, die wir _do_ haben, nämlich dass ein ReactChild ein ReactElement (ich habe type , um dies zu erkennen, aber Sie könnten es sein vorsichtiger, wenn Sie wollten). Wir können auch beschreibender und ausführlicher sein, dass Slot . Ich habe gesehen, wie eine UI-Bibliothek ein Muster implementiert hat, bei dem Klassen ihre eigenen Slots angehängt haben. Etwas wie:

<Dropdown>
  <Dropdown.Header>
    <FancyIcon>My Header!</FancyIcon>
  </Dropdown.Header>
  {dropdownItems}
</Dropdown>

Auf diese Weise ist dem Benutzer die Bedeutung jedes Slots klar. Der Rest der Kinder (die nicht als Slots geparst) werden in einer Sammlung namens children belassen und à la carte gerendert, sodass der Benutzer children normal verwenden kann, während er ein spezielles Verhalten für die . reserviert benutzerdefinierte Steckplätze.

BEARBEITEN: React hat anscheinend eine Methode React.isValidElement die Sie anstelle meiner benutzerdefinierten isReactElement .

BEARBEITEN: Sie können auch die Eigenschaft children in Ihrer Komponente eingeben, damit ein Benutzer Kontrollkästchen nur als Kinder übergeben darf. Dies wird dazu beitragen, diese Laufzeitfehler zu vermeiden.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen