Props & State Interfaces

In notebook:
FrontEndMasters Intermediate React
Created at:
2019-06-25
Updated:
2019-08-15
Tags:
React libraries JavaScript

https://frontendmasters.com/courses/intermediate-react-v2/props-state-interfaces/

Carousel

  //    ****        Carousel.tsx        ****

import React from "react";
 // **** 1. import Photo.  ↴
import { Photo } from "@frontendmasters/pet";


 // **** 3. add interface.  ↴
 // we haven't yet used interfaces...
 // you have to capitalise (by convention)
interface IProps {
  media: Photo[];
}

 // **** 4. interface.  ↴
interface IState {
  active: number;
  photos: string[]; // "string array"
}

 // **** 5. add the IProps and Istate.  ↴
class Carousel extends React.Component<IProps, IState> {
 // **** 2. add public.  ↴
 // this actually has to match
 // what's defined in
 // interface IState
  public state: IState = {
    photos: [],
    active: 0
  };
  // public
  public static getDerivedStateFromProps({
    media
   // **** 6. tell what the props are.  ↴
  }: IProps): { photos: string[] } {
    let photos = ["http://placecorgi.com/600/600"];

    if (media.length) {
      photos = media.map(({ large }) => large);
    }

    return { photos };
  }
  // public
  // **** 7. tell the event type.  ↴
  public handleIndexClick = (event: React.MouseEvent<HTMLElement>) => {
   // **** 8. when it's not an HTMLElement.  ↴
    if (!(event.target instanceof HTMLElement)) {
      return;
    }
   // **** 9. when index not defined.  ↴
   // TypeScript forces you to add these defensive code checks
   // that are a bit overkill
   // but at least you are sure that you won't have an error here
   // it's also a way of future proofing
    if (event.target.dataset.index) {
      this.setState({
        active: +event.target.dataset.index
      });
    }
  };
  public render() {
    const { photos, active } = this.state;
    return (
      <div className="carousel">
        <img src={photos[active]} alt="animal" />
        <div className="carousel-smaller">
          {photos.map((photo, index) => (
            // eslint-disable-next-line
            <img
              key={photo}
              onClick={this.handleIndexClick}
              data-index={index}
              src={photo}
              className={index === active ? "active" : ""}
              alt="animal thumbnail"
            />
          ))}
        </div>
      </div>
    );
  }
}

export default Carousel;

Do Pet.js

   //    ****        Pet.tsx        ****


 // **** 1. import what's needed .  ↴
import React, { FunctionComponent } from "react";
import { Photo } from "@frontendmasters/pet";
import { Link } from "@reach/router";

 // **** 3. define the interface.  ↴
interface IProps {
  name: string;
  animal: string;
  breed: string;
  media: Photo[];
  location: string;
  id: number;
}

 // **** 2. tell what FunctionComponent is.  ↴
const Pet: FunctionComponent<IProps> = props => {
  const { name, animal, breed, media, location, id } = props;

  let hero = "http://placecorgi.com/300/300";
  if (media.length) {
    hero = media[0].small;
  }

  return (
    <Link to={`/details/${id}`} className="pet">
      <div className="image-container">
        <img src={hero} alt={name} />
      </div>
      <div className="info">
        <h1>{name}</h1>
        <h2>{`${animal} — ${breed} — ${location}`}</h2>
      </div>
    </Link>
  );
};

export default Pet;