React + Typescript: props y parametros opcionales

Otro tema interesante a tener en cuenta es que no siempre queremos informar todas las propiedades que pida un componente hijo, algunas pueden ser opcionales, y en otros podemos querer alimentar valores por defecto.

Vamos a ver como podemos tratar ésto con TypeScript.

Manos a la obra

Vamos a añadirle una propiedad llamada saludo del tipo string al componente MuestraNombre

interface Props {
+ saludo: string;
  nombre: string;
  apellidos: string;
  onResetApellidos: () => void;
  onSetApellidos: (value : string) => void;
}

const MuestraNombre: React.FC<Props> = (props) => {
  return (
    <>
      <h1>
-       {props.nombre}
+       {`${props.saludo} ${props.nombre}`}
      </h1>
      <input value={props.apellidos} onChange={e => props.onSetApellidos(e.target.value)} />
      <button onClick={props.onResetApellidos}>Borrar Apellidos</button>
    </>
  );
};

Ahora en el componente padre podemos alimentar esta propiedad:

    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <MuestraNombre
        nombre={nombre}
        apellidos={apellidos}
        onResetApellidos={handleResetApellidos}
        onSetApellidos={handleSetApellidos}
+       saludo="Hola"
      />
    </div>

La mayoría de las veces vamos saludar con un 'Hola', podríamos hacer esta propiedad opcional y dejar decidir al que consume el componente si quiere cambiar el saludo, veamos como hacer ésto:

Primero hacemos la propiedad saludo opcional, añadiendo un interrogante.

interface Props {
-  saludo: string;
+  saludo?: string;
  nombre: string;
  apellidos: string;
  onResetApellidos: () => void;
  onSetApellidos: (value : string) => void;
}

Después, en el código vamos a comprobar que si nos viene una cadena en blanco mostramos el saludo 'Hola'.

const MuestraNombre: React.FC<Props> = (props) => {
  return (
    <>
      <h1>
-       {`${props.saludo} ${props.nombre}`}
+       {`${props.saludo ? props.saludo : "Hola"} ${props.nombre}`}
      </h1>
      <input value={props.apellidos} onChange={e => props.onSetApellidos(e.target.value)} />
      <button onClick={props.onResetApellidos}>Borrar Apellidos</button>
    </>
  );
};

Si borramos en el padre la propiedad:

export default function App() {
  const [nombre, setNombre] = React.useState("Jose");
  const [apellidos, setApellidos] = React.useState("García Pérez");
  const handleResetApellidos = () => {
    setApellidos("");
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <MuestraNombre
-       saludo="Hola"
        nombre={nombre}
        apellidos={apellidos}
        onResetApellidos={handleResetApellidos}
        onSetApellidos={setApellidos}
      />
    </div>
  );
}

Otra manera de abordar este problema, es alimentando valores por defecto en caso de que la propiedad no esté informada. Vamos a dejar el interrogante en la propiedad saludo, pero recuperando el código anterior para mostrar el valor por defecto:

const MuestraNombre: React.FC<Props> = (props) => {
  return (
    <>
      <h1>
-       {`${props.saludo ? props.saludo : "Hola"} ${props.nombre}`}
+       {`${props.saludo} ${props.nombre}`}
      </h1>
      <input value={props.apellidos} onChange={e => props.onSetApellidos(e.target.value)} />
      <button onClick={props.onResetApellidos}>Borrar Apellidos</button>
    </>
  );
};

Ahora lo que hacemos es indicarle que si la propiedad saludo no está definida, le asignaremos el valor por defecto 'Hola':

...

+ MuestraNombre.defaultProps = {
+   saludo: "Hola"
+ };

De esta manera no tenemos que ir manejando en el render del componente la asignación de valores por defecto.

Para terminar, vamos a limpiar el componente MuestraNombre. Si te fijas nos ha quedado manchado con un montón de props. ¿Y si aplicamos destructuring? Nos quedaría tal que así:

const MuestraNombre: React.FC<Props> = (props) => {
+ const {
+   saludo,
+   nombre,
+   apelllidos,
+   onSetApellidos,
+   onResetApellidos
+ } = props;

  return (
    <>
      <h1>
-       {`${props.saludo} ${props.nombre}`}
+       {`${saludo} ${nombre}`}
      </h1>
-     <input value={props.apellidos} onChange={e => props.onSetApellidos(e.target.value)} />
+     <input value={apellidos} onChange={e => onSetApellidos(e.target.value)} />
-     <button onClick={props.onResetApellidos}>Borrar Apellidos</button>
+     <button onClick={onResetApellidos}>Borrar Apellidos</button>
    </>
  );
};

TypeScript nos infiere además el tipado de las propiedades que hemos extraido con el destructuring.

¿Te apuntas a nuestro máster?

Si te ha gustado este ejemplo y tienes ganas de aprender Front End guiado por un grupo de profesionales ¿Por qué no te apuntas a nuestro Máster Front End Online Lemoncode? Tenemos tanto edición de convocatoria con clases en vivo, como edición continua con mentorización, para que puedas ir a tu ritmo y aprender mucho.