Carl
Carl

Reputation: 357

How can I pass props in Typescript?

So I started learning typescript and I can't figure out how to pass props. I have a movie search, so I have a next component chain: App -> Hero(a huge div which displays everything) -> Container(a container which contains divs with movies in each one) -> MovieItem(a div which contains all info about the movie, which we get from the json data).

I made the next interface:

export interface IMovie{
    rate: string;
    title: string,
    tagline: string;
    date: string;
}

Edit: I edited the code from the answers and that's what it looks like now:

App component:

    const App = () => {

  const [movies, setMovies] = useState<MovieProps[]>([]);

  useEffect(() =>{
      fetchMovies();
  }, [])

  async function fetchMovies() {
      try{
        let apikey = '0ce1e51fc4f5b92e2f8984a6aa026503';
        let url: string = 'https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=';
        url = url + apikey;
        const response = await axios.get<MovieProps[]>(url);
        setMovies(response.data);
        console.log(response.data);

      }catch(e){
        alert(e)
      }
  }

  return (
    <div className="App">
      <Header/>
      <Hero movies={movies}/>
    </div>
  );
}

export default App;

Hero component:

const Hero = ({movies}: MovieProps) => {
  return (
    <div className="hero">
        <h1>trending today</h1>
        <Container movies={movies}/>
    </div>
  );
};

export default Hero;

Container component:

const Container = ({movies}: MovieProps) => {
  return (
    <div className="container">
        <MovieItem movies={movies}/>
    </div>
  );
};

export default Container;

I passed props all the way through to MovieItem, but here's the problem, in App.tsx component I can't pass the array, because when I try to pass to the Hero component in the following way: <Hero movies={movies}/>, I get the next error:

Type 'MovieProps[]' is not assignable to type 'MovieType[]'.
  Type 'MovieProps' is missing the following properties from type 'MovieType': rate, title, tagline, date

I created an index.d.ts file to store all the interfaces, it looks like this:

declare module 'myMovies' {
    type MovieType = {
        rate: string,
        title: string,
        tagline: string,
        date: string,
    };

    interface MovieProps {
        movies: MovieType[],
      }
    }

    module.exports = {
        MovieType,
        MovieProps
    };

So I'm not sure what could be the problem, I declared a default array in useState() and tried to pass to the Hero component.

Upvotes: 1

Views: 2396

Answers (4)

AKX
AKX

Reputation: 168863

Your typings seem to be confusing a single movie and your props type, which has an array of multiple movies.

You also don't need an index.d.ts file at all; in fact you shouldn't have one. (You could export your common types from e.g. a types.ts file.)

Here's an example where everything you have is in one file.

import { useState, useEffect } from "react";

interface MovieType {
  rate: string;
  title: string;
  tagline: string;
  date: string;
}

interface MoviesProps {
  movies: MovieType[];
}

function App() {
  const [movies, setMovies] = useState<MovieType[]>([]);
  useEffect(() => {
    setMovies([{ rate: "8", title: "Hello", tagline: "Yes", date: "2021" }]);
  }, []);

  return (
    <div className="App">
      <Hero movies={movies} />
    </div>
  );
}

function Hero({ movies }: MoviesProps) {
  return (
    <div className="hero">
      <h1>trending today</h1>
      <Container movies={movies} />
    </div>
  );
}

function Container({ movies }: MoviesProps) {
  return (
    <div className="container">
      {movies.map((movie) => (
        <MovieItem key={movie.title} movie={movie} />
      ))}
    </div>
  );
}

function MovieItem({ movie }: { movie: MovieType }) {
  return (
    <div className="movie">
      <span className="movie__title">{movie.title}</span>
      <span className="movie__title">{movie.tagline}</span>
      <span className="movie__title">{movie.rate}</span>
      <span className="movie__title">{movie.date}</span>
      <button>Button</button>
    </div>
  );
}

Upvotes: 2

MWO
MWO

Reputation: 2806

Besides your movies typo mentioned in the comments, you did not assign the props type in the argument.

import React from 'react'

export interface IMovie{
  rate: string;
  title: string,
  tagline: string;
  date: string;
}

interface movieProps{
  movies: Array<IMovie>
}

const MovieItem = ({movies}: movieProps) => {  //here i have an error `Property 'movies' does not exist on type 'PropsWithChildren<movieProps>`
 return (
   <div>
        {movies.map(movie=>
          <div className="movie">
          <span className="movie__title">{movie.title}</span>
          <span className="movie__title">{movie.tagline}</span>
          <span className="movie__title">{movie.rate}</span>
          <span className="movie__title">{movie.date}</span>
          <button>Button</button>
          </div>
        )}
   </div>
 )
};

Upvotes: 0

HAK
HAK

Reputation: 458

try

const MovieItem = ({movies}):({movies:movieProps}) => { 
  return (
    <div>
          {movies.map(movie =>  //and here I have an error Parameter 'movie' implicitly has an 'any' type.
      <div className="movie">
      <span className="movie__title">{movie.title}</span>
        <span className="movie__title">{movie.tagline}</span>
        <span className="movie__title">{movie.rate}</span>
        <span className="movie__title">{movie.date}</span>
        <button>Button</button>
  </div>
      )}
    </div>
  );
};

The problem with declaring props for functional components in Typescript is that the underlying implementation of react expects the props to extend from their native, which is the error you are getting.

A solution is to describe prop in the component's definition rather passing props interface to the component generator(FC)

Upvotes: 0

Tom
Tom

Reputation: 1188

I tend to simply define components' props directly:

const MovieItem = (props: movieProps) => { 
  const { movies } = props;
  ...

I alos notice that you have no 'movies' prop, only a 'movie' prop. Check your spelling

Upvotes: 0

Related Questions