Definitelytyped: Нужны примеры React.Children.map () и React.cloneElement ()

Созданный на 26 окт. 2016  ·  8Комментарии  ·  Источник: DefinitelyTyped/DefinitelyTyped

React.Children.map() отсутствует хороший пример , я не понимаю, как его набрать:

/*
<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>
    );
  }
}

(очень распространенный пример, см. http://stackoverflow.com/a/32371612/990356)

  • Как набрать this.props.children ?
  • Как набрать React.cloneElement() ?
  • Как набрать React.Children.map() ?

В большинстве случаев я получаю следующую ошибку: Type 'string' is not assignable to type 'ReactElement<any>' , см. # 8131

Самый полезный комментарий

Вопрос:

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)
  })
);

Решение:

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

Все 8 Комментарий

отправьте запрос на перенос. Я пересмотрю это.

@vvakame Я не могу, я не понимаю, как это должно работать

я тоже. пожалуйста, поговорите с авторами определений.

вам нужно будет явно добавить аннотацию типа или утверждение типа checkbox потому что нет гарантии, что this.props.children не содержит значений, отличных от ReactElement. Например:

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

@vsiao спасибо большое, работает!

Вопрос:

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)
  })
);

Решение:

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

@tkrotoff, вы также можете сделать это:

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

Это и предлагаемое решение просто используют тот факт, что React.Children.map не является строго типизированным. С таким же успехом вы могли использовать any вместо типа Checkbox поскольку нет гарантии, что это будет тип потомков. Это означает, что у вас, скорее всего, возникнут ошибки времени выполнения, если кто-то не воспользуется компонентом, передав строку.

Как насчет этого:

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>
    )
  }
}

Что будет отображать:

My Header!
Welcome
This is my lovely component with slots

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

Этот подход использует информацию о типе, которую мы _do_ имеем, а именно, что ReactChild может быть ReactElement (я использовал type для обнаружения этого, но вы могли поосторожнее, если хотите). Мы также можем быть более наглядными и детально проработать Slot . Я видел, как библиотека пользовательского интерфейса реализует шаблон, в котором классы имеют свои собственные слоты. Что-то вроде:

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

Таким образом, пользователь понимает значение каждого слота. Остальные дочерние элементы (не проанализированные как слоты) остаются в коллекции с именем children и отображаются в виде корзины, что позволяет пользователю по-прежнему использовать children обычном режиме, сохраняя при этом особое поведение для пользовательские слоты.

РЕДАКТИРОВАТЬ: React, по-видимому, имеет метод React.isValidElement который вы можете использовать вместо моего пользовательского isReactElement .

РЕДАКТИРОВАТЬ: вы можете еще что-то сделать, это ввести свойство children в свой компонент, чтобы пользователю было разрешено передавать флажки только в качестве дочерних элементов. Это поможет избежать этих ошибок времени выполнения.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

victor-guoyu picture victor-guoyu  ·  3Комментарии

lilling picture lilling  ·  3Комментарии

csharpner picture csharpner  ·  3Комментарии

Loghorn picture Loghorn  ·  3Комментарии

Zzzen picture Zzzen  ·  3Комментарии