Búsqueda de sitios web

reactjs - Uniendo props de React con props personalizados


Al definir props en React, usando TypeScript, parece que las props predeterminadas de React se sobrescriben por lo que sea que defina la interfaz. ¿Hay una manera limpia de combinar ambos sin tener que especificar cada prop que React ya conoce?

Ejemplo :

interface IProps { // extends React.???
   title: string;
   // Generally seen adding children?: any
   // But this can get out of hand with onClick, onChange, etc
}

function MyComponent(props: IProps) {
   return props.children; // Typescript error: children doesn't exist on props
}

Respuesta #1

Lo que te refieres como "props por defecto de React", también conocido como "cada prop que React ya conoce", se llama más propiamente "props aceptadas por cualquier componente envoltorio de elemento DOM de React", es decir, onClick, className, etc.

Props predeterminados generalmente se refiere a la propiedad estática defaultProps en un React.Component, con la cual proporcionas valores predeterminados para cualquier prop que no haya sido pasada a tu componente.

onClick, className, etc. no son nombres de props reservados y puedes usarlos como quieras en tus propios componentes; por ejemplo, podrías hacer que tu componente espere que className sea una función (independientemente de si es una buena idea o no). Los únicos nombres de props reservados que funcionan en elementos de React de cualquier tipo (al momento de escribir esto) son key y ref, y en realidad no son verdaderas props porque no están disponibles para el método render de tu componente.

Pasar onClick a tu propio componente no registra automáticamente un manejador de clic. Solo lo hará si pasas el onClick que recibiste a un <div>, <button> u otro elemento del DOM de React que renderices en algún lugar. Si no haces nada con una propiedad que te fue pasada, no tiene efecto (además de posiblemente causar que un componente de renderizado puro se actualice cuando de otro modo no lo haría).

Por ejemplo, el siguiente componente no responderá a los clics:

const ClickFail = props => <button />
render(<ClickFail onClick={...} />, document.getElementById('root'))

Pero lo siguiente sí lo hará:

const ClickSuccess = props => <button onClick={props.onClick} />
render(<ClickSuccess onClick={...} />, document.getElementById('root'))

Y podrías pasar onClick a solo un subelemento si realmente quisieras:

const ClickButtonOnly = props => (
  <form>
    <input placeholder="ignores clicks" />
    <button onClick={props.onClick}>Handles Clicks</button>
  </form>
)

O podrías pasar varios manejadores de clic con diferentes nombres:

const SimpleForm = props => (
  <form>
    <button onClick={props.onCancelClick}>Cancel</button>
    <button onClick={props.onOKClick}>OK</button>
  </form>
)

También ten en cuenta que algunos envoltorios de elementos DOM aceptan props que otros no, por ejemplo, readOnly se aplica solo a <input> y <textarea>.

Incluso puedes exigir que los children sean del tipo que quieras. Por ejemplo, puedes pasar una función como los children de un componente y usarla (de nuevo, no es el mejor uso de React, pero solo para ilustrar lo que es posible):

type Props = {
  value: number,
  children: (value: number) => number,
}
const ApplyFunction = (props: Props) => (
  <div>{React.Children.only(props.children)(props.value)}</div>
)

render(
  <ApplyFunction value={3}>
    {value => value * value}
  </ApplyFunction>,
  document.getElementById('root')
)
// renders <div>9</div>

Entonces ves, IProps no necesariamente tiene que extender nada.

Sin embargo, es común pasar las propiedades restantes a un envoltorio de elemento DOM de React (por ejemplo, <div {...props}>...</div>) y, como estabas preguntando, sería bueno poder comprobar el tipo de todas esas propiedades de entrada de tu componente.

Creo que podrías hacer lo siguiente con Flow para comprobar los tipos correctamente, pero desafortunadamente no creo que haya un equivalente en Typescript (alguien que me corrija si me equivoco):

type Props = React.ElementProps<typeof 'div'> & {
  title: string,
}
const MyComponent = (props: Props) => (
  <div {...props}>
    {props.title}
  </div>
)

Respuesta #2

Deberías definir que tu componente funcional sin estado devolverá React.SFC<TusProps>.

Prueba esto

import * as React from "react";
const MyComponent: React.SFC<IProps> = (props) => {
  return props.children;
}

Si quieres usar un componente basado en clases, puedes extender tu clase con React.Component<TusProps(opcional), TuEstado(opcional)> en su lugar

Por ejemplo

import * as React from "react"
class MyComponent extends React.Component<IProps> {
  public render(): JSX.Element {
    return (
      <div>...</div>
    );
  }
}