LoginSignup
3
0

More than 3 years have passed since last update.

How to build a movie search app using React HooksをSvelteで試してみた

Last updated at Posted at 2020-12-01

はじめに

Svelte Advent Calendar 2020 2日目の担当です。
今回はReact HooksのTutorialであるHow to build a movie search app using React Hooks
をSvelteで試してみたいと思います。
React hooksの部分についてですが、今回はreact-hooks-in-svelteを参考に代替することにしています。

完成物

Svelte-Hooked

Tutorialを試してみる

Header.svelte


<script>
  export let title;
</script>

<style>
  .App-header {
    background-color: #282c34;
    height: 70px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: calc(10px + 2vmin);
    color: white;
    padding: 20px;
    cursor: pointer;
  }

  .App-header h2 {
    margin: 0;
  }
</style>

<header class="App-header">
  <h2>{title}</h2>
</header>

{title}になっている箇所はApp.svelteから値を渡すようにしています。
内容的にはReactのTutorialとほぼ変わらないですね。

Movie.svelte

<script>
  const DEFAULT_PLACEHOLDER_IMAGE =
  "https://m.media-amazon.com/images/M/MV5BMTczNTI2ODUwOF5BMl5BanBnXkFtZTcwMTU0NTIzMw@@._V1_SX300.jpg";
  export let movie;
  $: poster = movie.Poster === "N/A" ? DEFAULT_PLACEHOLDER_IMAGE : movie.Poster;
</script>

<style>
.movie {
  padding: 5px 25px 10px 25px;
  max-width: 25%;
}

@media screen and (min-width: 694px) and (max-width: 915px) {
  .movie {
    max-width: 33%;
  }
}

@media screen and (min-width: 652px) and (max-width: 693px) {
  .movie {
    max-width: 50%;
  }
}


@media screen and (max-width: 651px) {
  .movie {
    max-width: 100%;
    margin: auto;
  }
}
</style>

<div class="movie">
  <h2>{movie.Title}</h2>
  <div>
    <img
      width="200"
      alt={`The movie titled: ${movie.Title}`}
      src={poster}
    />
  </div>
  <p>({movie.Year})</p>
</div>

Movieを表示する部分になります。
ここでは$:Reactive Statementsを使用して値をリアクティブしています。
今回はmovie一つ一つのposterの有無を確認するような処理になっておりますが、一つのコンポーネント内でReactive Statementsを使うことも可能です。

Search.svelte

<script>
  export let query = '';
</script>

<style>
  .search > input[type="text"]{
    width: 40%;
    min-width: 170px;
  }

  .search {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 10px;
  }
</style>

<form class="search">
  <input
    bind:value={query}
    type="text"
    on:keyup
    placeholder="title" />
</form>

Search.svelteではbind:, on:ディレクティブを使用してApp.svelteに値を渡したり、関数を呼び出ししています。

App.svelte

<script>
  import Header from './Header.svelte';
  import Movie from './Movie.svelte';
  import Search from './Search.svelte';
  import { onMount } from 'svelte'
  const MOVIE_API_URL = "https://www.omdbapi.com/?s=man&apikey=4a3b711b"; // you should replace this with yours
  let query = '';
  let movies = [];
  let loading = null;
  let errorMessage = '';

  onMount(async () => {
    fetch(MOVIE_API_URL)
      .then(response => response.json())
      .then(jsonResponse => {
        if (jsonResponse.Response === "True") {
          movies = jsonResponse.Search;
        } else {
          errorMessage = jsonResponse.Error;
        }
    });
  });

  function searchMovies() {
    loading = true;
    fetch(`https://www.omdbapi.com/?s=${query}&apikey=4a3b711b`)
      .then(response => response.json())
      .then(jsonResponse => {
        if (jsonResponse.Response === "True") {
          movies = jsonResponse.Search;
          loading = false;
        } else {
      errorMessage = jsonResponse.Error;
          loading = false;
        }
    });
  }
</script>

<style>
  .App {
    text-align: center;
  }

  .App-intro {
    font-size: large;
  }

/* new css for movie component */

  * {
    box-sizing: border-box;
  }

  .movies {
    display: flex;
    flex-wrap: wrap;
    flex-direction: row;
  }

  .errorMessage {
    margin: auto;
    font-weight: bold;
    color: rgb(161, 15, 15);
  }
</style>

<div class="App">
  <Header title="SVELTE HOOKED" />
  <Search bind:query on:click={searchMovies} />
  <p class="App-intro">Sharing a few of our favourite movies</p>

  <div class="movies">
    {#if loading && !errorMessage }
      <span>loading...</span>
    {:else if errorMessage }
      <div class="errorMessage">{errorMessage}</div>
    {:else}
      {#each movies as movie}
        <Movie movie={movie} />
      {/each}
    {/if}   
  </div>
</div>

Tutorialに記載されているuseEffectの箇所は今回はonMountを使っています。
一覧表示の条件としてloadingがtrueであれば、loading...と表示して、エラーがあればエラー内容の表示、それ以外は一覧を表示するようになっています。
エラーメッセージはAPIレスポンスがTrue意外の時にErrorから取得しています。
bind:queryの部分ですが名前と値が一致する場合省略可能となるため、省略するようにしています。

おわりに

今回作成したAppをSvelte REPLにて公開しております。
最後にSvelteに少しでも興味を持った方はSvelte Tutorialをご覧ください!

参考資料

How to build a movie search app using React Hooks
react-hooks-in-svelte
Svelte API

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0