Definitelytyped: Necessita de exemplos de React.Children.map () e React.cloneElement ()

Criado em 26 out. 2016  ·  8Comentários  ·  Fonte: DefinitelyTyped/DefinitelyTyped

React.Children.map() está faltando um bom exemplo , não entendo como digitá-lo:

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

(exemplo muito comum, consulte http://stackoverflow.com/a/32371612/990356)

  • Como digitar this.props.children ?
  • Como digitar React.cloneElement() ?
  • Como digitar React.Children.map() ?

Na maioria das vezes, acabo com o seguinte erro: Type 'string' is not assignable to type 'ReactElement<any>' , consulte # 8131

Comentários muito úteis

Pergunta:

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

Solução:

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

Todos 8 comentários

envie uma solicitação de pull. Eu vou revisar isso.

@vvakame não posso, não entendo como deve funcionar

eu também. por favor, converse com os autores das definições.

você precisará anotar ou declarar checkbox explicitamente porque não há garantia de que this.props.children não contenha valores não ReactElement. por exemplo:

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

@vsiao muito

Pergunta:

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

Solução:

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

@tkrotoff você também pode fazer isso:

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

Esta e a solução sugerida estão apenas aproveitando o fato de que React.Children.map não é fortemente tipado. Você também pode ter usado any vez do tipo Checkbox pois não há garantia de que esse será o tipo dos filhos. Isso significa que você provavelmente terá erros de tempo de execução se alguém usar incorretamente o componente passando uma string.

Que tal agora:

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

Que irá renderizar:

My Header!
Welcome
This is my lovely component with slots

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

Esta abordagem tira proveito da informação de tipo que _temos_, que é que ReactChild pode ser ReactElement (eu usei type para detectar isso, mas você poderia ser mais cuidado se você quiser). Também podemos ser mais descritivos e elaborados que Slot . Eu vi uma biblioteca de IU implementar um padrão em que as classes tinham seus próprios slots anexados. Algo como:

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

Desta forma, o usuário tem clareza sobre o significado de cada slot. O resto dos filhos (aqueles não analisados ​​como slots) são deixados em uma coleção chamada children e renderizados à la carte, permitindo que o usuário ainda use children normalmente, reservando um comportamento especial para o slots personalizados.

EDIT: React aparentemente tem um método React.isValidElement que você pode usar no lugar do meu isReactElement .

EDIT: Outra coisa que você pode fazer é digitar a propriedade children em seu componente, de forma que um usuário só tenha permissão para passar as caixas de seleção como filhos. Isso ajudará a evitar esses erros de tempo de execução.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

jbreckmckye picture jbreckmckye  ·  3Comentários

demisx picture demisx  ·  3Comentários

jgoz picture jgoz  ·  3Comentários

fasatrix picture fasatrix  ·  3Comentários

JudeAlquiza picture JudeAlquiza  ·  3Comentários