React勉強会のノウハウを紹介します

こんにちは! Air メイトのフロントエンドエンジニアの @sadness_ojisanです。今月から「React 人材をどう育むか」という連載を行います、この記事はその第一弾です。

みなさーん!React 人材の採用はうまくいっていますか???私たちはまだまだ足りていません! そこで最近では、React 人材を登用するために、採用だけでなく育成も試みています。 この連載では実際に行なった育成プランとその効果を紹介できればと思います。

私は社内で 2-30 人規模の React 勉強会の講師をしたり、社外でもプログラミング講座の先生などをやっています。 また、大学院では教育学を専攻していたこともあり、育成はかなり関心がある分野です。

この記事では、React 勉強会を開催しているなか、「この教え方や工夫が効いたよ」といった指導方略や、「受講生はここでつまづいたよ」という経験を紹介します。

この記事に書かれていること

  • React 勉強会で、どういうカリキュラム・工夫を実施したか、それに対して受講生はどういう反応だったか
  • 受講生はどこでつまずいて、それに対してどのように対応したか(もしくはどう対応すべきだったか)

目次

  • 育成のゴールとコンテンツ
    • 参加希望者のスキルセット・何をベースとして育成を試みればいいのか
  • カリキュラムと React を教える時のコツ
    • jQuery の辛さを理解してもらう
    • MVC の辛さを理解してもらう
    • React の嬉しさを理解してもらう
    • 受講生からの反応
  • 運営のコツ
    • 説明の追記をできるような場所を作っておきましょう
    • 開発環境は CodeSandobox を採用する
    • CSS in JS の説明をしない
    • map と filter の例は先に書いておく
    • メンターは必須、3 人あたり 1 人のメンターを目安に
  • 結論

育成のゴールとコンテンツ

育成のゴールは、「React, Redux 案件を自立して開発できるエンジニアを輩出する」であり、React, Redux に興味がある方々を社内 Slack で募ってみました。 もともと Air シリーズと呼ばれるサービスの開発者を対象に声を掛けたのですが、他チームの開発者からも問い合わせがあり、結果としてホットペッパーグルメブッキングテーブルの開発者も含め、30 人ほど連絡がありました。

多くの方々が興味を持たれていましたので、育成のプランを分割し、それぞれを一日丸々使った集合型研修として実施することとし、まず React 勉強会なるものを開きました。

ちなみにこの後には、

  • 環境構築とエコシステムを理解しよう勉強会
  • API からデータを取ろう勉強会(Ajax, Promise, Async/Await, Axios を触ろう勉強会)
  • redux, react-redux 勉強会

が開催されます。

参加希望者のスキルセット・何をベースとして育成を試みればいいのか

リクルートの多くのサービスは JSP + jQuery / Rails + jQuery という構成で開発されており、多くのメンバーのスキルセットもそれらに即したものです。(最近はちょっとだけ React と Vue もあります。)

カリキュラムを決めるためにも参加希望者にスキルセットに関する簡単なアンケートを事前に行いました。

それによると、

  • 参加希望者のほとんどがフロントエンドエンジニア(他にはネイティブアプリエンジニア、サーバーサイドエンジニア、デザイナーが参加)
  • ES6 を業務で使っている人は 1/3 くらい

といったものでした。 そのため、ES5, jQuery の知識を持っていることを前提で、ES6, React を教えるカリキュラムを作成しました。

カリキュラムと React を教える時のコツ

React 開発でやるべきことは、「state を書き換えたら UI が変わる 、 f(state) = UI となるコンポーネントを宣言すること」だと私は思っています。 そこで、f(state) = UIはどういうことかを理解してもらうことを一番の狙いに置いて、カリキュラムを設計しました。

教材は Todo リスト作成を扱いました。いわゆる一般的な TodoMVC に即したものです。 しかし、ただ作るだけでは理解が深まらないと思ったため、ちょっとした工夫をしました。

はじめて React を学ぶ際、たくさんの概念や用語と出会います。 たとえば、state, props, setState, render, class などが出てきます。 そういった知らない言葉が次々に出てきてしまうと混乱しやすく、さらには何がわからないかすらも分からなくなってしまいます。 その結果、どれだけ勉強しても進歩や成長を感じることが難しくなり、学習のやる気が削がれてしまうことがあります。 もし学習のやる気がなくなってしまえば、その技術を身につけることができなくなります。

そこで、勉強会を運営する際には、学習のやる気をどう保つかということを考えて、次の 2 つの方法を考えました。

まず React を学んだ先に何があるかを明確にすることで、少しでも学び取ろうとする意思を固めるように仕向ました。 具体的には、jQuery で Todo リストを作る例を体験してもらうことで、React は Web アプリケーション開発における辛みを減らしてくれる仕組みを持っているということを、うっすらと感じてもらいました。(jQuery で作るには厳しい仕様や、わざとぐちゃぐちゃにした実装を渡しました。) 渡したコードはこちらで、React がないとどう苦しむかを体験できるようにしています。

todo一覧

そして、React の使い方を教える際には、登場させる用語や概念を精査し、それらを教える順序も調整しています。 それら 1 つ 1 つを教える中で、小さなクイズやハンズオンを用意し、進歩や成長を感じ取れるように設計しました。 さらに、つまずいても自力解決できる仕組みとして、動作するコードを虫食いにしたコードや、コメント付きの回答などをCodeSandboxで提供しました。 (1 日丸々使えるため、ハンズオンの時間を長めにとっています。)

こういう考えや工夫を凝らした上で、具体的には次のように講義とハンズオンを行いました。

jQuery の辛さを理解してもらう

講義は、僕が jQuery で無邪気につくった Todo リストとそのコードを見てもらうところから始まります。 このコードに対して、無邪気な点を指摘してもらうクイズ大会を行いました。 (※これは過去に僕が production で書いたコードをベースとしています。本人が書いたコードと知らず、目の前ですごいディスられが発生していました)

たとえば、コードの中には次のような箇所が潜んでいます。

1
2
3
4
5
6
7
8
9
10
11
12
var filterState = $("#filter-state").text();
if (filterState === "" || filterState === "on") {
  $("#filter-state").text("off");
  $("#todos-area input:checkbox:checked")
    .parent()
    .addClass("hide");
} else {
  $("#filter-state").text("on");
  $("#todos-area input:checkbox:checked")
    .parent()
    .removeClass("hide");
}

ここでは Gitterで作ったチャットルームに、このコードを見た感想や酷い点をたくさん書き込んでもらいました。 無邪気にコードを書くと「どこで何の処理をしているのかが見えなくなり、メンテナンスなどができなくなる」、「UI から状態を取り出して使っており View とロジックが密結合している」という問題点を指摘して欲しくて、待ち受けていました。

クソ実装に対するレビュー

ちゃんと、 UI から状態を取り出しているところが刺されました。

このようなクイズで、jQuery で実装するとどういう問題が起き得るかを体験してもらいました。

他にも、たくさん無邪気なところがあるので、もし興味がございましたら探して見てください

無邪気なコード:https://github.com/sadnessOjisan/HELL_todo_jquery

MVC の辛さを理解してもらう

先ほどのコードは、「UI から状態を取り出している」ことや「if 文のブロックの中で UI を更新するなど、どこで何がおきているのかの見通しを立てづらい」という問題があります。

次に、このコードを MVC で書き直したコードを見てもらい、そのコードに対しても問題点があることを指摘してもらおうと、また「どこがイケテナイ点でしょう」とクイズを出しました。。

受講生は「MVC 設計にしたことですでに問題は解決されている」と感じていたらしく、なかなか Gitter は盛り上がりませんでした。

MVC実装に対するレビュー

とはいえ、実際はこのようなコードが紛れ込んでいます。

1
2
3
4
async createTodo(task) {
  await TodoManager.createTodo(task);
  todoView.renderTodo(TodoManager.todos);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
renderTodo(todos, isFiltered = false) {
  const sortedTodos = todos.sort((a, b) => a.id - b.id);
  const filteredTodos = isFiltered
    ? sortedTodos.filter(item => item.isDone)
    : sortedTodos;
  $('#todos-area').empty();
  for (const todo of filteredTodos) {
    const isDone = todo.isDone;
    if (isDone) {
      $('#todos-area').append(
        `<p class="checked"><input checked type="checkbox" class="todo-check" id=${
          todo.id
        } /><span>${todo.task}</span></p>`
      );
    } else {
      $('#todos-area').append(
        `<p class="unchecked"><input type="checkbox" class="todo-check" id=${
          todo.id
        } /><span>${todo.task}</span></p>`
      );
    }
    $('.todo-check').on('click', e => {
      const clickedPoint = e.target;
      if (clickedPoint.id == todo.id) {
        todoController.updateTodo(e, todo);
      }
    });
  }
}

ここでいうイケテナイ点とは「コントローラーが毎回 Model にデータを問い合わせて、その結果を元に UI を書き換える処理を呼び出すことで、UI を更新している」「UI を書き換える時に DOM のすべてを書き換えてしまっている」点を指しています。

毎回 Controller が Model と View の橋渡しをすればコード増える上に、UI に変更があるたびに、View を全更新しているともったいないという説明をすると、受講生もイケテナイということを理解してくれました。

ソースコード: https://github.com/sadnessOjisan/ES6_todo_jQuery

そして、React はこの 2 つの問題を解決することを告げた上でランチにしました。 ここまでで、React は何を解決してくれるかを説明する準備ができました。

React の嬉しさを理解してもらう

午後からは React の使い方についての解説と実習をメインに取り組みました。

ここでの学習目標は、「React 開発の基本は、state を書き換えたら UI が変わる f(state) = UI となるコンポーネントを宣言することである」ということを理解してもらうことに絞っています。

新しい概念がたくさん出てくるので、React の概念や用語を教える順番にはかなり気を払いました。そこで、次の順序で教えてみました。

「Class Component を組み合わせて UI を組み立てる -> props で画面のデータを書き換える -> state から画面に描画するデータを作る -> setState()で描画するデータを書き換える -> イベントからsetState()を呼び出す -> props から関数・処理を取り出し実行することで、親のsetState()を呼び出す -> ライフサイクルに応じてsetState()を呼び出す」

ここで意識したことは、「state を書き換えたら UI が変わる」を伝えるために、state と setState() の説明を中心に沿えることです。

そして、ここで意図的に教えなかったことは、 react-dom, JSX, stateless function component, componentDidMount()以外のライフサイクルメソッドです。 これらは実際にはとても大切なものなのですが、f(state) = UI となるコンポーネントを作る」という説明には不要であるとの判断で、敢えて教えませんでした。 実務編の講習機会が今後あるため、今回の講習では React の考え方を教えることだけにフォーカスし、その他の話は次回以降に話すこととしました。 また、環境構築も教えておらず、 CodeSandbox を活用しました。環境構築にかかる時間をまるまる省けるので非常に助かりました。

講義で扱ったコードは https://github.com/sadnessOjisan/react-hello-world です。 実際の講義では、これを CodeSandbox 上でライブコーディングしていきました。

Component で UI をパーツごとに作る

まず class component で UI のパーツを作れること、そのパーツを組み合わせることでページを作れることを見せます。ここではヘッダーとボディを作りました。 ここは CodeSandbox 上でライブコーディングを行い、ヘッダーとボディをそれぞれ作って、1 つのページで表示させる例を見せました。 UI の最小パーツは何になるかを意識して説明しました。 このように、React.Component を継承したクラスに render メソッドを持たせて export すれば UI パーツになるという例をライブコーディングしました。

UI の最小パーツを作ってみせる例

1
2
3
4
5
6
7
8
9
10
import React, { Component } from "react";
class Hello extends Component {
  render() {
    const { name } = this.props;
    return <div className="hello-row">hello world</div>;
  }
}
export default Hello;
1
2
3
4
5
6
7
8
9
10
import React, { Component } from "react";
class Header extends Component {
  render() {
    const { name } = this.props;
    return <div className="header">header</div>;
  }
}
export default Header;

コンポーネントを UI パーツとして組み立てる例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, { Component } from "react";
import Header from "./Header";
import Hello from "./Hello";
class App extends Component {
  render() {
    return (
      <div className="wrapper">
        <Header />
        <Hello />
      </div>
    );
  }
}
export default App;

その後に CodeSandbox の URL を共有し、受講生に CodeSandbox で写経してもらいました。 このときに受講生にはフッターも作ってもらいました。

props を使うことでコンポーネントの中の表示を変える

次に props について教えます。呼び出されたコンポーネント(子)は、呼び出した元(親)から値を受け取ることができ、その値は子コンポーネント内で利用できます。 親は子供に props 経由で値を渡すことで、さまざまな UI を、1 つのコンポーネントから作れることを体験してもらい、UI パーツは再利用できる という嬉しさを感じてもらいます。

ここでは Hello world と表示させるだけでなく、Hello ${自分の名前} も表示してもらいました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, { Component } from "react";
import Header from "./Header";
import Hello from "./Hello";
class App extends Component {
  render() {
    return (
      <div className="wrapper">
        <Header />
        <Hello />
        <Hello name="john" />
      </div>
    );
  }
}
export default App;
1
2
3
4
5
6
7
8
9
10
import React, { Component } from "react";
class Hello extends Component {
  render() {
    const { name } = this.props;
    return <div className="hello-row">hello {name ? name : "world"}</div>;
  }
}
export default Hello;

state から UI を作る

次に state を説明します。 クラスコンポーネント 1 つ 1 つが state を持つことができ、それは

  • コンポーネント内の UI の表示に利用できること
  • 値として子コンポーネントに props 経由で渡してあげられること

を示しました。 ここでは、Hello ${自分の名前} の名前を state に保存しておき、それを表示させるライブコーディングを行いました。

予想では、ここで受講生の多くは混乱するはずであり、事実多くの方が混乱していました。 そこでの混乱は、「state がどうして必要になるんだ」という混乱です。

state を書き換える

そこで、React はf(state) = UI を意識すべきという話を再び行います。実は、 state を書き換えるだけで UI を変えることができるという話をしました。 そして state を書き換える仕組みsetState()について解説します。

ここではライブコーディングで、${名前}の好きな寿司: ${ネタ名}と表示されているコンポーネントをクリックすると、ネタ名がいろんな名前に変化する例を見せました。(onClickからsetState()を呼び出しました。) このとき、受講生の納得度を高めるために、コントローラーを書いていないことを強調しています。 jQuery で MVC をしている際は、UI を更新する処理を毎度コントローラーから呼び出していたことを何度も強調しました。

ちなみに onClick は普通の HTML + JS でも出てくるので、onClick をいきなり登場させても問題はありませんでした。

関数・処理を props 経由で渡す

次にカウントアップ・カウントダウンボタンを作るライブコーディングを見せます。 目的はイベントハンドリングと、ハンドラーの受け渡しの理解です。

カウントアップ・ダウンのボタンは、同一のコンポーネントを使い回せるように意識して作例としています。

1
2
<CountButton operator='+' handleClick={()=>this.countUp() />
<CountButton operator='-' handleClick={()=>this.countDown() />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { Component } from "react";
class CountButton extends Component {
  render() {
    const { operator, handleClick } = this.props;
    return (
      <div className="count-button" onClick={handleClick}>
        <p>{operator}</p>
      </div>
    );
  }
}
export default CountButton;

そして、 コンポーネントが再利用できていることを強調しました。 そして、親は子に、関数や処理も渡しており、子コンポーネントで props.handleClick() で親から渡された関数を実行できることを示します。

ライフサイクル

最後にライフサイクルの紹介です。ここでは componentDidMount() 時に alert 文を出すだけにしました。 一般的に、componentDidMount()時にはコンポーネントを初期化でき、それに伴って state の書き換えもできることを伝えますが、具体例は API への fetch などでの非同期処理と合わせて説明した方が嬉しさは伝わりそうであるため、詳しく説明はしませんでした。 受講生の中には Ajax に慣れていない方が多数いることも事前アンケートで知っていたため、そもそも API を叩くことをカリキュラムに含めませんでした。 ただし、ハンズオンはしていませんが、componentDidMount()時に API からデータを取得して state を初期化する例は見せています。

Todo ハンズオン

ここまできたら todo リストの実装を行なってもらいます。

todo-reactを目指して開発を行なってもらいました。

ここではコースを 3 つ、

用意しました。

初心者コースでは、永続化やデータの fetch 部分、Class 記法やimportなどはすでにコーディングしてあり、JS そのものに詳しくなくても、React を書けるようにしています。

ハンズオンではtodo-reactの開発の質問をメンターに聞きながら、開発してもらいました。

受講生からの反応

理解度調査としてアンケートを講習後にとりました。

14 件という少ない回答数のため参考にならないかもしれませんが、このような結果が出ています。

愚直な jQuery がなぜ辛いかについて

地獄jQueryの理解度

MVC jQueryがなぜ辛いかについて

MVCjQueryの理解度

React を使うと何が嬉しいかについて

Reactの嬉しさについての理解度

React の使い方について

Reactの使い方について

ハンズオンで受講生がつまづいたポイント

JavaScript の文法

実は、ハンズオンでは想像以上に質問が上がりました。そしてその多くは ES6 や JavaScript そのものの記法に関するものでした。 とくに、Array.prototype.filter()()=>{}this.hoge でつまづく人が多く、その説明をしっかりすべきであったことを反省しています。

イベントハンドラー

私のサンプルコードでは関数を子供へ渡すときに、onClick={(e)=>this.hoge(e)}などとして渡していました。 もちろんこれはパフォーマンスが悪いなどと指摘される書き方ではあるのですが、constructorthisbindしなくて済むため、thisのややこしい話を避けられると思いこの書き方を採用していました。 しかし受講生自身が Google などで調べたときに、onClick={this.hoge}のような記法にたどり着き、どのような書き方が正しいかで混乱をしていたようです。

そのため、イベントハンドラーの説明では、記法の種類も含めて行うべきだったと反省しています。

state と props の違い

React に詳しい方々は「何いってるんだお前」と思われるかもしれませんが、実はこの混乱は私自身も経験があるものです。 まず、state も props もデータとして UI に表示できる点で共通しています。 そして props を使うコンポーネント自身も、state を定義できるため、どうして子コンポーネントで state を使わないのかという疑問が生まれます。

これに対しては、「確かに子コンポーネントでも state を定義できるが、する必要がないから設定しません。この場合、子コンポーネントの役割は、親からもらった値を表示するだけです。もし、props で渡ってくるデータを書き換えたい場合は親の state の setState() を呼び出せば良くて、その方が管理する state を 1 箇所に集中させられるので、管理が楽になるよ」というメリットを説明しました。

運営のコツ

説明の追記をできるような場所を作っておきましょう

補足説明を垂れ流せる Slack や Gitter のチャンネルを必ず用意しておきましょう。 どれだけ万全のカリキュラムを作っても、受講生の理解度は毎回バラバラなので、どんな質問も受け付けられる・回答できる場所として、タイムラインのようなものを用意しておくと良いでしょう。口頭でした説明の記録に使えます。 ちなみに社用 Slack は、フロアから PC を持ち出せないという人もいるかもしれないので、使えないかもしれませんね。 その場合は GitHub のアカウントさえあれば使えるチャットツール Gitter がオススメです。

質問掲示板

開発環境は CodeSandobox を採用する

CodeSandbox を利用することを強く推奨します。 これまでハンズオンは nvm & npm で行なっていました。

1
2
3
$ npm install
$ npm run start

さえすれば動く箱を用意していて挑むのですが、なぜかいつも環境構築ができない人に出会っていました。

そこで今回は、CodeSandboxを実験的に使ってみたのですが、誰も環境構築でつまずかなかったのでとても助かりました。

また CodeSandboxには React の雛形もすでに用意されており、npm scripts や react-dom の説明も無しで React の説明を始められました。

初心者向けハンズオンの際にはCodeSandboxを利用することをオススメします。

CSS in JS の説明をしない

React の説明がメインであったためスタイリングは、考えたくなかったのですが、スタイリングは絶対に受講生が気になると思いましたので、css ファイルをimportすればclassNameを指定してスタイルを当てられる例を見せました。 CodeSandboxの上では webpack の css-loader を気にすることなく css をimportできるため、可能な方法です。

もちろんこれは大規模になればclassNameが衝突して、スタイルが崩れてしまいます。 一般的な React 開発では CSS in JS を可能とするライブラリを使って、class に Hash をつけることで衝突を避けますが、簡単に導入できるライブラリが思い当たらなかったので、CSS in JS の説明は省いています。

(業務では styled-jsx, style-components を利用していますが、styled-jsx は babel-plugin が必要になる、styled-components はこれからはじめて React の Component を学ぶ人にとっては記法によって混乱を来すと判断し、採用しませんでした。)

map と filter の例は先に書いておく

フロントエンド開発経験者だとしても、Array の組み込みメソッドに詳しくない場合があります。 たとえば Vue.js を採用しているプロジェクトの場合は map の代わりに v-for ディレクティブを使っていたり、 lodash を採用しているプロジェクトの場合は Array の組み込み関数自体に馴染みがなかったりします。React 開発では、データからコンポーネントを生成する際に mapfilter は多用しますが、Array の組み込み関数でつまづくというケースに何人かがはまっていました。React の説明は通じているのに Array メソッドで混乱するというケースがあったため、注意をもっと払うべきでした。

ハンズオンではGitterに組み込み関数例を書くことで乗り切りました。

mapfilter

メンターは必須、3 人あたり 1 人のメンターを目安に

上記のように JS そのものでつまづくといった問題もあり、実はハンズオンでは多くの質問が出ていました。 React の説明であれば資料や回答を参照することで解決できるのですが、JS そのものの質問の場合は、資料を用意しておらず即座に自力で解決してもらうことが難しかったです。

ただ、今回のハンズオンでは、React 経験者を 7 人ほど集めており、先生役として質問に答えてもらったりしていました。 だいたい、受講生 3 人あたり 1 人の先生という比率です。 この比率だと、受講生全員の質問を即座に捌くことができました。

当日、メンターとして手伝ってくださった @YuG1224さん、@jaxx2104さん、@kt3kさん、@wilf_genyaさん、@nabepon_devさん、@JfipKenさんありがとうございました。

メンターのみなさま

結論として

いかがでしたでしょうか。今回取り組んだ Tips をまとめると次の通りです。

  • 同じ題材を jQuery と React で書くことで、React を使わない辛さを経験してもらうと、React を使う理由を納得できる
  • ハンズオンでは CodeSandbox を使う。環境構築にかかる時間を削減できるだけでなく、React 開発に必要な周辺ライブラリを教える必要がなくなる。
  • 座学は、「class componentを組み合わせて UI を組み立てる -> props で画面のデータを書き換える -> state から画面に描画するデータを作る -> setState()で描画するデータを書き換える -> イベントからsetState()を呼び出す -> props から関数・処理を取り出し実行することで、親のsetState()を呼び出す -> ライフサイクルに応じてsetState()を呼び出す」の順番で教えると、f(state) = UIはどういうことかを理解してもらえる
  • とはいえ、実際に React を身につけてもらうためには、実習が効果的である。メンターは受講生 3 人に対し 1 人いると、受講生の質問にしっかりと対応することができる。

もちろん施策に対する効果測定を行なったわけではないため、「こういう教え方をしたからこういう結果が出る」と言えるものではありません。 しかし、少なくとも弊社ではこのような講座を開くことで、受講生の React 理解を促進することはできました。次回の開催も期待されているらしく、

実務編への参加希望

さらには、React を使った実務にも挑戦をしたいという意見が多かったので、

Reactの仕事希望

次は、React 勉強会実務編として、環境構築講座、Redux, react-redux 講座を開催します。 正直、今回開催した講座だけだと、実務で手伝える範囲はとても限られていると思っています。 しかし、自分で React 環境を作れるようになる(= つまり JS のエコシステムを理解する)、Redux を扱えるようになると、実務に入るだけの必要最低限の知識は得られていると言えるでしょう。

これらの講座は 2 月に行う予定であり、また開催後にカリキュラムや効果といったものを共有できればと思います。

最後に、リクルートライフスタイルでは、エンジニア採用を絶賛行なっております。 フロントエンドエンジニアの職種もあるため、興味がある方はぜひこちらから申し込みをお願いします。 もちろん、これからフロントエンドエンジニアになりたいという方の相談もできるので、気軽にどうぞ! あと、僕のチームの FE 開発リーダーのポジションが空いてます。(2019/01 現在) 良いリーダーに巡り会いたいので、こちらもどうぞ、よろしくお願いします!

採用 HP:https://engineer.recruit-lifestyle.co.jp/recruiting/