React Tutorial: Movie App 🎬
Tags: javascript, react, typescript, tutorial
Published: 2020-10-30 20:14:21

Hello 👋, For my first post on dev.to, I decided to share with you a small tuto about creating a Movie App with React and TypeScript.

> ### Demo [https://movie-app-majuran.netlify.app](https://movie-app-majuran.netlify.app) 🚀

Source code available on Github : [Here !](https://github.com/Maj07/movie-app-react)

## Setup 
Before starting coding let's setup our project.

### Generate project with CreateReactApp: 
I often (to not say always 😁) use [Create React App](https://github.com/facebook/create-react-app) to initiate my React projects. 

In order to generate our project run :

```bash
npx create-react-app my-movie-app --template typescript
```

### API :
The frontend will have to fetch the data from an API, I choose [TMDb]() 🎬 : It's free, you just need to create an account to get your API key 

Please use your API key as an environment variable, in `.env` file :
```yml
REACT_APP_API_KEY=YOUR_API_KEY
```

The configuration is done, let's start coding. 💻

## State Management
Our app components are going to communicate between them.
To handle this, we need a state management, for that, we are going to combine the `context API` and `state hooks`

> In case, you are not familiar with react hooks. I encourage you, to read ➡️ [React Hooks](https://reactjs.org/docs/hooks-intro.html) and [Context API](https://reactjs.org/docs/context.html)

```jsx
// create context
export const MoviesContext = React.createContext<{
  movies: Movie[];
  updateMovies: Function;
}>({
  movies: [],
  updateMovies: Function,
});

function App() {
// get default values
  useEffect(() => {
    discoverMovies()
      .then(setMovies)
      .catch((_) => setMovies([]));
  }, []);

// use state hook
  const [movies, setMovies] = useState<Movie[]>([]);

  return (
  // Provide to the all project: 
  // - movies an array of Movie, 
  // - updateMovies : function to update the movies list
    <MoviesContext.Provider value={{ movies, updateMovies: setMovies }}>
      <div className="App">
        <Header></Header>
        <Catalog></Catalog>
      </div>
    </MoviesContext.Provider>
  );
}

```

## Components 

> *Don’t be afraid to split components into smaller components.* ([React Doc](https://en.reactjs.org/docs/components-and-props.html#extracting-components))

In React it's important to split the UI by components, so let's check how many components do we need : 
![Screenshot](https://dev-to-uploads.s3.amazonaws.com/i/23jkc2cs12a8oj7udhfw.png)

 As you can see, three components are standing out: 
* `Header`
* `Search`
* `Catalog`

A question to ask when creating a component: 
*What my component should do ?!* 🤔

### Header
The header component is pretty simple, it contains the project title and the Search component.  

```typescript
export const Header = () => {
  return (
    <div className="header">
      <h1 className="header__title">Movie App</h1>
      <div className="header__search">
        <Search></Search>
      </div>
    </div>
  );
};

```

### Search
The search component should: 
1. show an input field
2. save user search
3. query the API with this search
4. update the movie list

In order to save the state of our input, we will use in this component the `useState` hook.

```jsx
export const Search = () => {
  const [search, setSearch] = useState("");
  // Consume our context to get updateMovies function
  const { updateMovies } = useContext(MoviesContext); 

  const handleOnSubmit = (event: React.FormEvent) => {
    event.preventDefault();
    if (search) {
      searchMovies(search).then((movies) => {
        updateMovies(movies);
      });
    }

  return (
    <div>
      <form name="form" onSubmit={(e) => handleOnSubmit(e)} noValidate>
        <input
          type="text"
          name="movie"
          className="search__input"
          placeholder="Search movie ... "
          value={search}
          onChange={(e) => setSearch(e.target.value)}
        />
      </form>
    </div>
  );
};

```

### Catalog
The catalog component should: 
1. get movies from the context
2. loop on this array

```jsx
export const Catalog = () => {
  // Consume the context to get list of movies.
  const { movies } = useContext(MoviesContext);

  return (
    <div className="catalogContainer">
      {movies.map((movie) => (
        <div className="catalog__item" key={movie.id}>
          <div className="catalog__item__img">
            <img src={movie.picture || imgPlaceholder} alt={movie.title} />
            <div className="catalog__item__resume">{movie.resume}</div>
          </div>
          <div className="catalog__item__footer">
            <div className="catalog__item__footer__name">
              {movie.title} ({new Date(movie.date).getFullYear()})
            </div>
            <div className="catalog__item__footer__rating">{movie.rating}</div>
          </div>
        </div>
      ))}
    </div>
  );
};

```
## fetch() data from API
In order to fetch the API for data, let's create a service : 

```jsx
const movieApiBaseUrl = "https://api.themoviedb.org/3";

export function searchMovies(search: string): Promise<Movie[]> {
  return fetch(
    `${movieApiBaseUrl}/search/movie?query=${search}&api_key=${process.env.REACT_APP_API_KEY}`
  )
    .then((res) => res.json())
    .catch((_) => {
      return [];
    });
}

export function discoverMovies(): Promise<Movie[]> {
  return fetch(
    `${movieApiBaseUrl}/discover/movie?sort_by=popularity.desc&api_key=${process.env.REACT_APP_API_KEY}`
  )
    .then((res) => res.json())
    .then((response) => mapResult(response.results))
    .catch((_) => {
      return [];
    });
}
```

## Conclusion
With this tutorial, I tried to present to you some important concept like : 
- React Context API
- React hooks
- function fetch to make API calls

## To go further
Some more ideas to improve your skills:
- handle No result
- use [Redux](https://redux.js.org/)
- pagination


## Source : 
- You can clone this project on [Github](https://github.com/Maj07/movie-app-react)