Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

はじめに

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

izumiiii
趣味でSvelte.js業務ではRuby on Railsを使っています。 Ruby/Ruby on Rails/AWS/Docker/CI/Svelte.js Contributor: Svelte.js, MDsveX
metaps
メタップスは「テクノロジーでお金と経済のあり方を変える」というミッションのもと、テクノロジーをフル活用することで人々を現実世界における様々な制約から解放し、世界中の誰もが自由に価値創造できる社会を目指しています。
https://metaps.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away