【第10回】これまでのまとめとこれから 〜ICPC直前!激励会Edition〜

ICPCも控え区切りがついたので、これまでの講義の振り返りと、今後のお話をします。

Homeブログ一覧【第10回】これまでのまとめとこれから 〜ICPC直前!激励会Edition〜

前回の復習

前回は、Reactの基礎を学びました。

ReactはFacebookが開発したJavaScriptのライブラリで、世界で最も人気のあるフロントエンドのライブラリとして知られています。

まずはコンポーネントという概念を学びました。 今までのHTMLは、複数の同じ要素や同じ構造を持つ要素がある場合、それぞれに対して同じような記述を繰り返していました。 しかし、Reactでは「コンポーネント」という概念を用いることで、これらを省略してひとまとまりに記述することができます。

ReactではJSXというJavaScriptの拡張構文を用いて、HTMLを記述します。 JSXでは、JavaScriptの中にHTMLを記述することができます。(厳密には違う)

次に、簡単のために「状態」をリロードしなくてもサイト内で変化するものと説明しました。 その上で、状態を管理するために、useStateという関数を使うことを学びました。 書き方は、const [変数名, 変数を変更する関数名] = useState(初期値)です。

たとえばカウントアップするアプリを作りたかったら、以下のように書きます。

counter.jsx
function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>カウントアップ</button>
    </div>
  );
}

課題の解説

まずはいままでkeyとしてindexを使っていましたが、それだと課題がうまくいかないので、keyを管理するための固有のidをTodoに追加します。

src/App.jsx
import { useState } from "react";

function App() {
  const [todoInput, setTodoInput] = useState("");
- const [todos, setTodos] = useState(["掃除", "洗濯", "買い物"]);
+ const [todos, setTodos] = useState([
+   { id: 1, title: "掃除" },
+   { id: 2, title: "洗濯" },
+   { id: 3, title: "買い物" },
+ ]);

  function handleChange(event) {
    setTodoInput(event.target.value);
  };

  function handleClick() {
-   const newTodos = [...todos, todoInput];
+   const newTodos = [...todos, { id: new Date().getTime(), text: todoInput }];
    setTodos(newTodos);
    setTodoInput("");
  };

  return (
    <div>
      <input type="text" value={todoInput} onChange={handleChange} />
      <button onClick={handleClick}>追加</button>
-     {todos.map((todo, i) => <p key={i}>{todo}</p>)}
+     {todos.map(todo => <p key={todo.id}>{todo.text}</p>)}
    </div>
  );
};

export default App;

keyは、Reactが要素を管理するために必要なものです。 keyは、要素が一意に識別できるものであれば何でも構いません。 今回は、idを使うことにしました。 idが一意なものであればいいため、今回はnew Date().getTime()を使っています。 new Date()は現在の日時を表すオブジェクトを返します。getTime()は、その日時をミリ秒で表した数値を返します。 同じミリ秒に投稿することはないと割り切って、idとして使うことにしました。(厳密に一意を担保する場合、DBでidを管理する、uuidを使うなどの方法があります)

課題1 Todoを削除する処理を実装

src/App.jsx
import { useState } from "react";

function App() {
  const [todoInput, setTodoInput] = useState("");
  const [todos, setTodos] = useState([
    { id: 1, title: "掃除" },
    { id: 2, title: "洗濯" },
    { id: 3, title: "買い物" },
  ]);

  function handleChange(event) {
    setTodoInput(event.target.value);
  };

  function handleClick() {
    const newTodos = [...todos, { id: new Date().getTime(), title: todoInput }];
    setTodos(newTodos);
    setTodoInput("");
  };

+ function handleDelete(id) {
+   const newTodos = todos.filter(todo => todo.id !== id);
+   setTodos(newTodos);
+ };

  return (
    <div>
      <input type="text" value={todoInput} onChange={handleChange} />
      <button onClick={handleClick}>追加</button>
-     {todos.map((todo,i) => <p key={i}>{todo}</p>)}
+     {todos.map(todo => (
+       <p key={todo.id}>
+         {todo.title}
+         <button onClick={() => handleDelete(todo.id)}>削除</button>
+       </p>
+     ))}
    </div>
  );
};

export default App;

まずは、handleDelete関数を作ります。 指定されたidを持つTodoを削除する処理を書きます。 filterという関数を使うことで、条件に一致する要素だけを残すことができます。 今回は、idが一致しないものだけを残すようにしています。 -> つまり、指定されたidを持つTodoを削除する処理を書いています。 これを使って、削除ボタンを押したときに、指定されたidを持つTodoを削除する処理を書きます。

todo配列のループの中で各idに対応した削除ボタンを設けることで、削除ボタンを押したときに、そのidを持つTodoを削除する処理を実装できます。

課題2 Todoを完了済みにする処理を実装

今回は拡張性を考えて完了・未完了の状態を切り替えられるようにしてみます。

src/App.jsx
import { useState } from "react";

function App() {
  const [todoInput, setTodoInput] = useState("");
  const [todos, setTodos] = useState([
-   { id: 1, title: "掃除" },
-   { id: 2, title: "洗濯" },
-   { id: 3, title: "買い物" },
+   { id: 1, title: "掃除", done: false },
+   { id: 2, title: "洗濯", done: false },
+   { id: 3, title: "買い物", done: false },
  ]);

  function handleChange(event) {
    setTodoInput(event.target.value);
  }

  function handleClick() {
    const newTodos = [...todos, { id: new Date().getTime(), title: todoInput }];
    setTodos(newTodos);
    setTodoInput("");
  }

  function handleDelete(id) {
    const newTodos = todos.filter((todo) => todo.id !== id);
    setTodos(newTodos);
  }

+ function handleDone(id) {
+   const newTodos = todos.map((todo) => {
+     if (todo.id === id) {
+       // idが一致するtodoのdoneを反転させる
+       todo.done = !todo.done;
+     }
+     return todo;
+   });
+   setTodos(newTodos);
  }

  return (
    <div>
      <input type="text" value={todoInput} onChange={handleChange} />
      <button onClick={handleClick}>追加</button>
      {todos.map((todo) => (
        <p key={todo.id}>
          {todo.title}
+         <button onClick={() => handleDone(todo.id)}>
+           {todo.done ? "X" : "O"}
+         </button>
          <button onClick={() => handleDelete(todo.id)}>削除</button>
        </p>
      ))}
    </div>
  );
}

export default App;

まずは、todoの要素にdoneというプロパティを追加します。 これは、Todoが完了済みかどうかを表すプロパティです。 初期値はfalseにしておきます。(未完了)

handleDone関数を作ります。 指定されたidを持つTodoのdoneを反転させる処理を書きます。 mapという関数を使うことで、配列の要素を変換することができます。 今回は、idが一致するものだけdoneを反転させるようにしています。 -> つまり、指定されたidを持つTodoが完了していれば未完了に、未完了であれば完了にする処理を書いています。

さいごに、Todoの完了ボタン表示をdoneの状態に応じて変えるようにします。 donetrueであればXfalseであればOを表示するようにしています。

課題3 並び替え機能を実装

src/App.jsx
import { useState } from "react";

function App() {
  const [todoInput, setTodoInput] = useState("");
  const [todos, setTodos] = useState([
    { id: 1, title: "掃除", done: false },
    { id: 2, title: "洗濯", done: false },
    { id: 3, title: "買い物", done: false },
  ]);

  function handleChange(event) {
    setTodoInput(event.target.value);
  }

  function handleClick() {
    const newTodos = [...todos, { id: new Date().getTime(), title: todoInput }];
    setTodos(newTodos);
    setTodoInput("");
  }

  function handleDelete(id) {
    const newTodos = todos.filter((todo) => todo.id !== id);
    setTodos(newTodos);
  }

  function handleDone(id) {
    const newTodos = todos.map((todo) => {
      if (todo.id === id) {
        todo.done = !todo.done;
      }
      return todo;
    });
    setTodos(newTodos);
  }

+ function handleSort() {
+   const newTodos = [...todos].sort((a, b) => {
+     if (a.done === b.done) {
+       return 0;
+     }
+     if (a.done) {
+       return 1;
+     }
+     return -1;
+   });
+   setTodos(newTodos);
+ }

  return (
    <div>
      <input type="text" value={todoInput} onChange={handleChange} />
      <button onClick={handleClick}>追加</button>
+     <button onClick={handleSort}>並び替え</button>
      {todos.map((todo) => (
        <p key={todo.id}>
          {todo.title}
          <button onClick={() => handleDone(todo.id)}>
            {todo.done ? "X" : "O"}
          </button>
          <button onClick={() => handleDelete(todo.id)}>削除</button>
        </p>
      ))}
    </div>
  );
}

export default App;

handleSort関数を作ります。 Todoを並び替える処理を書きます。 [...todos]とすることで、todosのコピーを作成しています。(コピーしないとsort関数でtodos自体が並び替えられてしまうため) sortという関数を使うことで、配列の要素を並び替えることができます。 今回は、donetrueであるものを後ろに、falseであるものを前に並び替えるようにしています。

これらのコードは全てこちらのリポジトリのkadaiというブランチにあります。もしわからないところがあれば参考にしてください。

これまでのまとめ

Webがどんなものかを学びました。Webの分野や技術・基本的な用語についても学びました。 HTML / CSS を使って実際にどんなに簡単にサイトが作れるかを体験しました。

本格的なサイトを作るために、JavaScriptを学びました。 JavaScriptを使うことで、サイトに動きをつけることができます。 素のJavascriptをあえて触ってもらうことで、初めからライブラリを使わずにプログラミングをすることの難しさを体験してもらいました。

GitGithubを使って、コードを管理する方法を学びました。 これらの技術は本当に大切なもので、プログラミングをしていて、これらの技術を使わないということはほぼありません。 なのでWeb研ではありますがあえてコードを資産として管理するという話や開発のためにチェックポイントを作るという話をしました。

Web研の目的は、チーム開発を通して会社や研究室などの実務活動で使えるスキルを身につけることです。 本来チーム開発は弊学では3年後半から行われますが、これを初期に知っておくか、知らないかで大きな差が生まれます。

データベースというものを学びました。 これも第二回の目的と同じように、より素の技術を知って触ってもらいたいという観点から、SQLという言語を使って直書きでデータベースを操作する方法を学びました。

アプリケーションサーバーというものを学びました。ここで初めてバックエンドという文脈が出現しました。 ユーザーに見えない部分ですが、Webサイトを作る上で欠かせないものです。この分野は特に極めがいがあって

  • パフォーマンスチューニング
  • 保守
    • マイクロサービス
    • クリーンアーキテクチャ
    • テスト駆動開発
    • ドメイン駆動開発
  • セキュリティ
  • データベース

色々な技術の知識が必要になってきますし、とても挑戦的で面白い分野です。

フロントエンドというものを学びました。バックエンドの対になるもので、ユーザーに見える部分です。 ここで初めて、データベースからバックエンドを経由してフロントエンドにデータを渡すということを学びました。

フロントエンドの続きです。今回は、ユーザーが投稿できるようにしました。 CRUDをフルスタックで実装することで、Webサイトを作る上での基本的な流れを学びました。 これで、Webサイトを作る上で必要な技術を全て浅く広く渡りました。

最後に前回、今回とReactというライブラリを使って、フロントエンドを実装してみました。 今までのDOM操作(HTMLを書き換える操作)よりも圧倒的に簡単に、かつ高速にフロントエンドを実装できることを体験してもらいました。

インタラクティブなお話をしましょう

ここまでの講義を振り返って、いろいろ聞きたいことがあります!

リンクとQRコードを貼っておきますので、好きな端末で開いてください。

slidoのQRコード

(2~30分くらいワークショップします)

このリンクは当日限り有効なので見返している人はとばしてください。

今後のプログラマとしての歩き方

今後どうやって自分を伸ばしていくか、どうやってプログラマとしてのキャリアを積んでいくかを説明します。 あくまでも、私(@sor4chi)の独断と偏見ですので、参考程度に聞いてください。 そこまで自分が卓越した能力を持ってる人間ではないですが、これまでの経験を踏まえて簡単に話させてください。

学び方について

まずは、学び方についてです。

あえて今までの講義は広く浅く早くを意識して進めてきました。 一回でこの進度をできるはずはないです。ここまで早くやっててついてこれてればすごいと思います。 (正直毎日3人くらいしか残らないと思ってましたし...)

第一回にも話したように、このWebもといプログラミング業界は常に情報発信がさかんに行われています。 そのため、キーワードさえ知っていればすぐに情報を取得し、知らない分野に飛び込んでいくことができます。

ボキャブラリを増やしつつ大まかなアプリケーション開発のイメージや流れを掴んでもらうことを目的としていたので、このような進め方をしました。

いま「投稿サイト作ってよ」と言われたらとりあえず「バックエンドフロントエンドに分けて作ればいいんだな。」とか、「データベースにデータを保存して、アプリケーションサーバーを経由してフロントエンドにデータを渡すんだな。」とか、「Reactを使えばフロントエンドを簡単に実装できるんだな。」とかくらいは思いつくと思います。

そんな感じで自分で調べられるような環境に持ってってやることを意識してたんですよね。

作りたいもの駆動でやるとうまくいきます。

Webで使う王道な言語はパッと思いつくだけで6種類、さらにそれぞれにライブラリやフレームワークがあって、それぞれを一から読んでおぼえようなんてやってるとキリがないです。

なので、作りたいものを決めて、それを作るために必要な技術を学ぶというのがいいと思います。 わざわざ全て学ぶ必要はないです。必要になったら調べればいいです。

先輩の使い方

知識の引き出し・索引として使う

たとえば「〇〇作ろうとおもってて、XXという構成を考えているんですがいい案ありますか?」とか「〇〇の機能を実装するためにXXのような仕組みを考えたんですけどうまくいかず困ってます。」とか。 結局こういうのって経験がものをいうんですよね。もちろん失敗することも大切なんですが、プログラミングってすごい時間のかかるものなので方針を間違うと時間が浪費されてしまいます。 なので設計や実装を考えたらそれを先輩に見てもらうというのはとても有効な手段だと思います。

コードレビューをしてもらう

コードレビューとは、自分が書いたコードを先輩に見てもらって、改善点を指摘してもらうことです。 これは、自分のコードを見てもらうことで、自分のコードに対する客観的な評価を得ることができます。 実務をしていると必ずPull Request単位でコードレビューを行います。 それを先輩にやってもらうことで、実務でのコードレビューに慣れることができますし、質の高いコードの書き方を学ぶことができます。

道標にする

〇〇エンジニアになりたいと決まれば、なるべく最短経路で進みたいですよね。 そういうときにどういう順序で学ぶとうまくいくか、やりやすいかを知っている人に聞いて道標にするというのはとても有効だと思います。

連絡

次回は休みです、ICPCに参加される皆さん頑張ってください!!!