読者です 読者をやめる 読者になる 読者になる

LIVESENSE made*

リブセンスのエンジニアやデザイナーの活動や注目していることをまとめたブログです。

MENU

事業を推進するために必要なエンジニアスキル

こんにちは、新卒2年目エンジニアの片岡です。 正社員転職メディア『ジョブセンスリンク』の開発を行っています。

job.j-sen.jp

リブセンスには職種の『越境』文化が根付いています。 セールスに必要なデータは営業担当者が自らSQLを書いて用意します。 エンジニアがディレクター的な働きをして機能設計に深く関わることはリブセンスにおいて自然です。

このような環境下で、私は『開発者の立場から事業を推進する』という指針を持って他職種の方たちと協働しながら開発を行ってきました。

『事業を推進するエンジニアに求められるスキル・姿勢とは?』という自らに課した問いに対して、新卒としてリブセンスで2年間を過ごした経験からたどり着いた自分なりの答えを共有させていただきます。

事業を推進するエンジニアに求められるスキル・姿勢とは

1.危機感を持って技術を学び続ける

入社当初、事業を推進するためには事業ドメインの理解やマネジメントなど「技術以外の総合力」が求められると私は思っていました。

しかし実務経験を経た今考え直すとむしろ逆で、事業を推進するエンジニアにこそ技術が求められるのだと感じています。 設計・コーディング・プロジェクト管理など様々な技術がなければ中長期的に見て効率的な開発を続けることができないからです。 変化の早い業界ですから、継続的に新技術のキャッチアップを行うことも必要です。

課題に対し、解決できる術をもたないエンジニアは等しく価値がありません。 事業を推進するエンジニアにとって、特定の技術スタックにこだわらず様々な技術領域に理解があることは必要不可欠だと思っています。

いま私は、「様々な技術領域に対して、広く、かつ浅くない理解を得る」という指針を持って継続的な技術的キャッチアップを心がけています。

2.自分の給与以上の価値を生み出すコードを書く

あなたが今日書いたコードは、今日もらった給与より価値を生みますか?

これは私が先輩からかけられた質問です。

エンジニアなら誰しも、技術的に正しいことをしたいと思っています。 メンテナブルで、再利用性が高く、完結で分かりやすいコーディングを誰しもが心がけています。 また、技術的にエキサイトしたいと思っています。

しかし、自分が書いたコードが自分の給与以上の価値を生み出しているか意識しながら開発している方はどれくらいいるでしょう?

例えば営業の方々は、個人として直接売上に責任を追っています。 それくらいの当事者意識を、私は持っていませんでした。 生み出す価値は、売上でなくとも社会への影響など異なるもので構いません。

if文のネストを気にするのと同じくらい自分の書くコードの価値を考えよう、と私は意識しています。

3.スピードvs品質の二項対立に囚われない

開発を行う中で、品質に関するトレードオフが発生します。 すなわち、品質を捨ててスピードを取るべきか速度を落として品質を高めるべきか、という議論です。

業務の中で、私はどちらの選択肢も見ました。 品質に倒したプロダクトは、事業的に求められる期限を守ることができないことがありました。 スピードに倒したプロダクトは、のちに破綻して『負債』と呼ばれています。

スピードvs品質のトレードオフでは、問題は解決しないことを私は学びました。

その上で、重要なのは「守るべき品質は何か」を見極めることだと思っています。裏を返せば「捨てる品質」の見極めです。

ミッションクリティカルであったりボトルネックになりやすいと思われるところは、エンジニアの職務として徹底的に品質に倒します。 逆に要件が変わりやすかったり、作り直すのが容易であるような部分については、容赦なく品質を落とすことも必要だと思っています。

4.他職種から信頼を得られる仕事をする

さきほど、「品質を見極めて容赦なく捨てることも必要」ということを書きました。 これは関係者の協力なしにはできません。

例えば、日時の入力フォームがあった場合、ファーストリリースに間に合わせることを優先して外部ライブラリのUIを使ったとします。 これは一般にデザイナーにとって面白くありません。 サイトカラーやUI設計があって、その中に外部ライブラリの違和感あるデザインが入り込むのはエンジニアにとっても好ましくはありません。

だとしても、必要なのは「入力フォームに日時が入力できる」ことであって、きれいで独自性が高く入力しやすい日時入力フォームを提供することではありません。(その入力フォームがUX上重要な役割を担うような場合は別ですが)

この場合、チームのデザイナーの職務ややりたいことを聞き、その上で自分たちが提供するものの本質的な価値について話し合い方針を決める必要があります。

私が担当した新規機能開発の事例では、ファーストリリースではデザインを妥協していただき、ABテストでデザインをいじるタイミングで1から綺麗に作り変えました。 このように、他職種の立場にたって対話することを心がけ、信頼を勝ち取ることも事業を推進するエンジニアになる上では重要なことだと思っています。

詳しくはDevLOVEというイベントの登壇資料にまとめてありますので、もしよければ読んでみてください。

speakerdeck.com

おわりに

『事業を推進するエンジニアに求められるスキル・姿勢とは』というテーマで4つの観点を紹介しました。

少しでもみなさんの開発の役に立てば幸いです。

良いチーム作りが成果創出につながった事例

f:id:livesense-made:20170104150246j:plain

皆さんはご自分のチームが、成果を生み出し続ける「最高に良いチーム」だと思いますか?

最近よくそんなことについて考えを巡らせている、リブセンスの風間です。 ジョブセンスリンクアプリ開発チームで、ディレクター兼プロダクト・オーナー(PO)をしています。 サービスを提供するユーザーさんのこともチームメンバーのことも幸せにしたいと欲張りながら奮闘し、失敗したり喜んだりする毎日を送っています。

昨年11月22日にリブセンス社内で「アプリ大会議」が開催されました。 アプリ大会議とは、ネイティブアプリに関わるリブセンス社員が互いの知見を持ち寄り、楽しく交流を深めて仲良くなることを目的としたLT大会です。 今回は、その中で私が発表した「良いチーム作りが成果創出につながった事例」をご紹介したいと思います。

POになんてなりたくなかった

今なら胸をはって「POの仕事が楽しいです!」と言えますが、実はPO就任した当時は嫌で嫌で…。正直に打ち明けると、とても怖かったんですね。 f:id:livesense-made:20170104154738j:plain   理由は以下2つ。

  • アプリの知見が社内になく、数字を伸ばせる自信がなかったから
  • リーダーの経験がなく、チームをリードできるか不安だったから

それをどうやって良い方向に持っていくことができたか? 今回はテーマを後者の「チームビルディング」に絞って、どんな取り組みをしてきたか、それがどう成果に繋がったかをお話したいと思います。

「自分よりも優秀なメンバーたち」

当時のチームにアサインされたメンバーは、全員が年上で経験/スキル豊富な人たちでした。一方で、POの自分はアプリ社内開発も未経験なら、リーダーになるのも初めてという頼りなさ。 こんな自分が信頼してもらえるだろうか…と強く不安を感じたことを覚えています。しかし、成果をあげて目標をクリアするためにはメンバーの協力が不可欠。気持ちを切り替えて、優秀なメンバーに協力してもらえるリーダーになるためにどうしたらいいか一つずつ考えていくことにしました。

「仲間のことをすきになる」

まずは、皆のことをすきになることに決めました。そうすることで本気で相手を理解し、協力し合いやすくなると考えたからです。 やってみたのはこのあたり。

  1. 接触頻度を増やす(1on1定期開催、理由をつけてランチ等)
  2. 相手の情報を知る(すきなこと、きらいなこと等)
  3. 相手の名前を沢山呼ぶ(リアルでもチャットでも)
  4. 共通体験を増やす(納涼船に乗る/UNOで遊ぶ等)

それぞれ効果を感じましたが、中でも意外とよかったのがUNOです。チームビルディングにUNO?と思われるかもしれませんが、年齢も職種も超えて盛り上がれるUNOは互いの距離を縮めるのに効果抜群。チームの雰囲気が一気によくなって、仕事も進めやすくなりました。その後、新メンバー加入後のUNOはチームの恒例行事になっています。

f:id:livesense-made:20170104163017j:plain

「ひとりひとりの考えを理解する」

次に、メンバーのことをより深く理解するために、1人ずつじっくり話をきかせてもらう時間を作りました。 何が好きで、どんな仕事をしてる時が楽しいか、ここで何を実現させたいか…等を1つずつ尋ねていき、これまでの失敗経験なども教えてもらいます。 そして、教えてもらった内容をもとに、皆の思いを実現させつつ同時にプロダクトも伸ばせる方法がないか考え、一つずつ実行してみることに。実際にどんなことを行ったかは、次のSTEP.1~3でご説明していきます。

STEP.1 戦いにそなえる

f:id:livesense-made:20170104153459j:plain シニアエンジニアさんが一番心配していたのが、リリースを急ぎすぎたことによる今後の運用に耐えられない実装状態でした。まずは私自身が課題を把握するために、専門的な話は「例え話」を使ってもらいながら理解して、修正工数を確保。一部機能を作り直す決断もしました。その結果、改善スピードを妨げる「負」が取り除かれ、機能追加がしやすくなりました。これまであまりここに工数を割くPOがいなかったとのことで、この件はとても喜ばれ、この時に信頼関係ができはじめたように思います。

STEP.2 作戦はみんなで

f:id:livesense-made:20170104150433j:plain 企画に参加したいという声をうけ、企画案はできるだけ早い段階で皆に相談することを徹底しました。「こんなことをやりたいけどどう思う?」と手描き案を共有し、皆の知恵をもらって案を改善することで、開発着手時には「これは僕/私が一緒に考えた企画だ」と思える状態を目指します。その結果、「アプリはPOがつくるもの」→「アプリをつくるのは私たち」という意識が根付き、チームメンバー全員が自分ごと感を持って改善に取り組むようになりました。 また、企画職だけでは考えつかない優れた案がうまれやすくなる、開発側が早めに要件を把握できて開発がスムーズになる等の効果もありました。

STEP.3 見積もりは信頼してまかせる

f:id:livesense-made:20170104155559j:plain 開発方針を考えるのが楽しい、設計が好きだという意見も大切にしました。 私はよく「締切に厳しい」と言われますが、自分から締切日を指定しません。私がやるのは、この施策は何のためにやるのか、次は何が控えているか、目的とロードマップをしっかり伝えて全員に理解してもらうことだけです。いつまでにできるか/どう作るべきかはメンバーを信頼して任せ、開発方針には細かく口出ししないようにしています。(このやり方がいいかどうかは、チームのメンバー構成にもよると思います。)

たまに、そのやり方だとゆるいスケジュールを引いてラクしようとされるんじゃないかと聞かれることもありますが、実際に皆が目的を充分に理解した上で自発的に考えるスケジュールは、毎回ビックリする程スピード感のあるもの。「POに言われたから急ぐ」ではなく、「自分でやるべきだと思った基準で全力でやる」という意識がチーム内に根付いた結果、事業部内の他のチームに驚かれる早さで改善サイクルが回るようになりました。

自分ごと感をもったチームは強い!

こうしてチーム全体が前のめりになって、リリースはどんどん高速になっていき、新しい機能が次々とアプリ内に追加されるようになりました。全員で議論を重ねながら改善を重ねた施策の効果は順調に伸び、季節が変わる頃には当初のチーム目標を大きく超える成果を出すことにも成功しました。

はじめは周囲に心配されていた小さなチームでしたが、少しずつ社内で事例共有させていただく機会も持てるようになり、2016年上半期にはリブセンスベストチーム賞にノミネートして頂くまでに成長しました。 f:id:livesense-made:20170104163055j:plain

さいごに

これらの取り組みの中で、個人的にとても嬉しかった「忘れられない出来事」があります。

弊社では定期的にエンジニア向けのサーベイを実施して、開発メンバーから意見や不満を拾い上げるということをしているのですが、なんとなくそのサーベイ結果を眺めていたある日、回答の中に自分に向けられたメッセージがあることにふと気が付いたのです。

【質問項目】仕事仲間は責任をもって精一杯クオリティの高い仕事をしているか【回答】 自分がアサインした時点でかなり不利な状況を押し付けられても前向きに日々改善を繰り返しているPOは素晴らしい。正直、自分の成績のみを考えたら違う案件や部署にいったほうが正解だと感じる。そうした案件にもかかわらず、また、その分野について詳しい知識を持っていないにも関わらず、ここまでサービスを成長させた手腕と責任感はすごい。

f:id:livesense-made:20170106210954j:plain

我武者羅に走りながらも常に不安を感じていた自分にとって、これはとても心に染み入る言葉でした。

チーム全員で力を合わせて戦うことに懸命になり続けた結果、それが成果につながり、お互いへの信頼や尊敬にもつながったことを実感したこの数ヶ月。今後は、さらに大きな成果を生み続けるチームであれるよう、POとしてもっともっと成長せねばと思います。

さて、そんな私たちジョブセンスリンクアプリ開発チームですが、次に掲げた新たな夢は大きく、目標達成への道のりは険しく、会社への貢献度だってまだまだです。今日も目黒の片隅のビルで、「転職経験の乏しいユーザーにこのUIは使いやすいだろうか?」「この機能は本当にユーザーに役立つだろうか?」と真剣に議論を重ねながら、最高に使いやすい転職アプリを目指して開発をしています。もしもあなたがスマートフォンで気軽に転職活動をしてみたいなとちょうどお考えでしたら、ぜひ一度このアプリを試していただけますと大変嬉しく思います。

itunes.apple.com

play.google.com

リブセンスで働くのは面白そうかもしれないな、という考えが少し頭をよぎったあなたは、ぜひ以下よりお気軽にご連絡ください。社員一同、あなたにお会い出来ることをたのしみにお待ちしております。

recruit.livesense.co.jp

ということで… 最後まで読んでくださり、ありがとうございました。

【本発表スライドを全て見たい方はこちら】

speakerdeck.com

Livesense式 開発合宿マニュアル part1

開発合宿

f:id:taise:20161112151149j:plain

Analyticsグループの大政です。
普段はデータ分析基盤や、レコメンド・エンジンの開発をしています。

開発合宿、やっていますか?
Livesenseでは、仕事の一環で開発合宿をすることもあれば、有志で集まって週末に開発合宿をすることもあります。
これまで5回ほど有志で開発合宿を行ってきた経験を元に、開発合宿を成功させるためのノウハウをお話したいと思います。

今回は、(1)なぜ開発合宿をやるのか、(2)どんなスタイルがあるのか、(3)おすすめの施設はどこかについてご紹介したいと思います。

続きを読む

レガシーコードの最適化とPHPバージョンアップ

PHP 開発 運用

f:id:boscoworks:20161021132156p:plain

 少し前のことになりますが、正社員転職サービス「ジョブセンスリンク」を構成するPHPアプリケーション群のPHPバージョンアップ対応と、それに合わせてレガシーコードの大幅な整理を行いました。
 「PHPのバージョンあげて、リファクタリングしたんだ」と一言で言えば簡単ですが、日々のサービス改善を滞らせず、システムのリニューアルを同時に進めていくのは多大な労力を要しました。
 今回はその仕事を主に担当した、キャリア事業部技術基盤チーム*1の海野がお届けします。お手柔らかにどうぞ。

ミッション

 PHPのバージョン問題。レガシーコードの山積。システムが歳を重ねるにつれ、必ず直面する大きな問題です。
 システムは、初めてリリースされた数年前の数倍の規模になっているでしょう。
 売上を支えるシステムを維持し、事業を加速させる施策を阻むこと無く、システムのリニューアルを進める。これが今回のミッションとなりました。
 このミッションを進める上で解決したかったのは、以下の2点です。

  • PHPバージョンアップ
  • 全社共有ライブラリの最適化

 今回は、この課題に対する取り組みについてお話します。

*1:ジョブセンスリンクのインフラ管理やシステム開発を主に担当しています

続きを読む

ReduxにおけるGlobal stateとLocal stateの共存

フロントエンド Redux 開発 React.js

初めまして!エンジニアの米山と申します。 転職会議ではフロントエンド開発にReact.jsとReduxを利用しています。 今回はReact, Redux開発におけるGlobal stateLocal stateという考え方について、軽く紹介させていただきます。

Redux開発の難点

ReduxはSingle source of truthという原則を採用しており、アプリケーションの状態は1つのオブジェクトに格納されます。それゆえ、アプリケーションの状態が散らばることなく管理が楽になります。

ただし、その弊害としてstateが肥大化します。stateが肥大化すると、reducerが肥大化する可能性が高まります。

対応策としては、reducerを分割したりNormalizrのような便利なツールを使う方法が考えられます。

しかし、React自身が提供するState管理を併用することで、Reduxの責務範囲をより適切化できる可能性があります。そして結果的にstateの肥大化を多少は抑制できるかもしれません。

それがGlobal stateとLocal stateの共存です。

Global stateとLocal state

今回の記事は以下のディスカッションを参考にさせていただきました。

Redux and global state vs. local state

Global state, Local stateという考え方は上記のディスカッションで使われているものであり、この記事はその紹介、という形になります。その上で事例を作成してみて、所感を載せています。

気になる方はぜひ元記事をご覧ください。

Global stateとは

Global stateは単にReduxの管理下に置かれるstateです。

どこからでも参照できるという意味でのGlobalではなく、アプリケーションレベルで管理される程度の意味合いですね。

Local stateとは

一方でGlobal stateではない(Reduxの管理外であるような)stateをLocal stateと呼びます。

最もカンタンな例は、React.Componenrtを継承したクラスを使って管理される状態でしょう。

Local stateの例

Local stateの例としては、コンポーネントに閉じるような状態が挙げられるかなと思います。

たとえば、あるコンポーネントがクリックされた回数を考えます。もしこのクリック回数という状態が、コンポーネントの中でしか使われない(たとえばrender関数内で表示するのみ)としたら、Reduxで管理せずとも良さそうです。

逆に言うと、クリック回数がアプリケーション全体に影響するならReduxに任せるべきかもしれません。

実例で見るGlobal stateとLocal stateの共存

では、ここからはGlobal stateとLocal stateの共存方法について、実例を見ていきたいと思います。

前提環境

まずは、React + Reduxによるアプリケーション作成環境を整えます。

お試しに使う程度なので、こちらのボイラープレートを使うと良いでしょう。

TrySpace/simple-redux-boilerplate

$ git clone git@github.com:TrySpace/simple-redux-boilerplate.git

内容はシンプルなカウンタアプリケーションで、最低限のRedux stateフローが作成されています。

$ git cloneが終わったら、ディレクトリをリネームしておくと良いでしょう。

$ mv simple-redux-boilerplate local-state-trial

パッケージをインストールして、npm startコマンドを打てば、開発に入ることができます。

$ cd local-state-trial
$ npm i
$ npm start

http://localhost:3000/ にアクセスすると、以下のようなアプリケーションが立ち上がります。

f:id:livesense-made:20160926162230p:plain

マイナスあるいはプラスのボタンをクリックすると、カウンタがデクリメントあるいはインクリメントされます。

Global state

Global stateについては、今回は既に作成済です。

// ./src/reducers/counter.js
export default function counter(state = 0, action) {
  switch (action.type) {
    case INCREMENT_COUNTER:
      return state + 1;
    case DECREMENT_COUNTER:
      return state - 1;
    default:
      return state;
  }
}

カウンタの状態に関して、Reduxが管理を行っています。なのでpropsを通じて、他のコンポーネントから現在のカウント数を参照することができます(Local stateの場合は、その子コンポーネントからのみ参照可能)。

Local state

では、Local stateを作成していきましょう。 ここでは、カウンタの表示・非表示を切り替える機能を付けてみます。表示であるか非表示であるかがここでのstateですね。

こちらが現在のカウンタコンポーネントです。

import React, { Component, PropTypes } from 'react';

export default class Counter extends Component {
  constructor(props, context) {
    super(props, context)
  }

  handleIncrement() {
    this.props.actions.increment()
  }

  handleDecrement() {
    this.props.actions.decrement()
  }

  render() {
    return (
      <div className="counter-container">
        <div className="counter-num-label">{this.props.counter}</div>
        <div className="counter-even-label">{this.props.counter % 2 === 0 ? 'even' : 'odd'}</div>
        <br />
        <div className="counter-buttons">
          <button onClick={() => {this.handleDecrement();}}>-</button>
          <button onClick={() => {this.handleIncrement();}}>+</button>
        </div>
      </div>
    )
  }
}

Counter.propTypes = {
  counter: PropTypes.number.isRequired,
  actions: PropTypes.object.isRequired
}

まあ、普通のReactコンポーネントですね。

ちなみに、Koba04さんの「Reactの最新動向とベストプラクティス」によりますと、最近では状態を持たないコンポーネントはStatelessFunctionalComponent(ステートレス ファンクショナル コンポーネント)を使うと良いらしいです。

import React from 'react'

const Hoge = (props) => {
  return (
    <div>
      {props.hoge.body}
    </div>
  )
}

export default Hoge

そしてもし、コンポーネントが状態を持つようになったら、React.componentを継承したclassを使おうねとのこと。

import React from 'react'

class Hoge extends React.Component {
  constructor(props, context) {
    super(props, context)
    this.state = {
      hoge: {
        body: '本文'
      }
    }
  }

  render() {
    return (
      <div>
        {this.state.hoge.body}
      </div>
    )
  }
}

なので、Local stateを扱うには、StatelessFunctionalComponentではなく、React.Componentを継承したクラスを用います。

では、先程のカウンタコンポーネントに、表示・非表示のLocal stateを持たせてみます。

export default class Counter extends Component {
  constructor(props, context) {
    super(props, context)

    // Local stateを定義
    this.state = {
      isVisible: true
    }
  }

  // 表示・非表示の切り替えアクション
  toggleVisibility() {
    this.setState({
      // 表示・非表示を反転
      isVisible: !this.state.isVisible
    })
  }

  handleIncrement() {
    this.props.actions.increment()
  }

  handleDecrement() {
    this.props.actions.decrement()
  }

  render() {
    // 表示 or 非表示
    const isVisible = this.state.isVisible ? 'block' : 'none'
    return (
      <div className="counter-container">
        {/* ↓ 表示・非表示stateに応じて、CSSで表示を操作 */}
        <div className="counter-num-label"  style={{display: isVisible}}>{this.props.counter}</div>
        <div className="counter-even-label">{this.props.counter % 2 === 0 ? 'even' : 'odd'}</div>
        <br />
        <div className="counter-buttons">
          <button onClick={() => {this.handleDecrement();}}>-</button>
          <button onClick={() => {this.handleIncrement();}}>+</button>
        </div>
        
        {/* 表示・非表示の切り替えアクションを発行するボタン */}
        <button onClick={() => {this.toggleVisibility() }}>
          {this.state.isVisible ? '閉じる' : '開く'}
        </button>
      </div>
    )
  }
}

コード中にコメントを書きましたが、以下、個別に見ていきます。

Local state定義

contractor 関数内でLocal stateを定義します。

constructor(props, context) {
  super(props, context)

  // Local stateを定義
  this.state = {
    isVisible: true
  }
}

これはReact, Reduxというより、JavaScriptのclassの話ですね。class内から、this.stateの形で参照することができます。

参照: MDN - class

Local state変更アクションの定義

Local stateを変更するアクションを定義します。

// 表示・非表示の切り替えアクション
toggleVisibility() {
  this.setState({
    // 表示・非表示を反転
    isVisible: !this.state.isVisible
  })
}

toggleVisibilityが呼ばれると、setStateが行われてカウンタコンポーネントが再レンダリングされます。

参照: Component API - setState

Local stateをrender関数内で利用

class内で定義したLocal stateとactionをrender関数内で利用します。

render() {
  // 表示 or 非表示
  const isVisible = this.state.isVisible ? 'block' : 'none'
  return (
    <div className="counter-container">
      {/* ↓ 表示・非表示stateに応じて、CSSで表示を操作 */}
      <div className="counter-num-label"  style={{display: isVisible}}>{this.props.counter}</div>
      <div className="counter-even-label">{this.props.counter % 2 === 0 ? 'even' : 'odd'}</div>
      <br />
      <div className="counter-buttons">
        <button onClick={() => {this.handleDecrement();}}>-</button>
        <button onClick={() => {this.handleIncrement();}}>+</button>
      </div>
      
      {/* 表示・非表示の切り替えアクションを発行するボタン */}
      <button onClick={() => {this.toggleVisibility() }}>
        {this.state.isVisible ? '閉じる' : '開く'}
      </button>
    </div>
  )
}

コードについては以上です。 コンポーネント内に閉じたstate及び、actionを作成することができました。要は素のReactの世界ですね。これがReduxのGlobal stateと共存しています。ポイントは、お互いが干渉すること無く共存している点ですね。

挙動

きちんとGlobal state(カウンタのインクリメント・デクリメント)と、Local state(カウンタの表示・非表示)が共存しているのがわかります。

f:id:livesense-made:20160926162321g:plain

状態管理をRedux外に漏らすことについて

Reduxの公式FAQに、以下のような項目があります。

Do I have to put all my state into Redux? Should I ever use React's setState()?

アプリケーションの状態管理を全てのReduxに任せるべきか、あるいはReactのsetStateも使っていくべきなのか。というものです。

返答としては、以下の通り。

There is no “right” answer for this. (中略) Find a balance that works for you, and go with it.

正しい答えはないので、良い塩梅を自分で探ってみてね。とのこと。 Reduxサイドとしても「全ての状態管理をReduxで行うべき」とは考えていないようです。

2016/09/29 追記:

Reduxの作者のDan AbramovがReduxの用法についてMedium.comに記事を投稿していました。

You Might Not Need Redux - Medium.com

Reduxを使わずともReduxのアイデアを取り入れることはできるし、 Local state is fine. とのことです。

Local stateを実現するツール

先程のリンクの中で、Local stateを実現するためのツールがいくつか挙げられています。

ただしどれもdecoratorを使う必要があるので、慣れていない人は今回紹介したような手法でもいいのかなと思います。

Local stateを使うべき時

では、どんなときにLocal stateを使うべきなのでしょうか。 個人的には、UIに関わる状態はLocal stateに向いていそうだなと思います。例としては、

  • 上下キーによるセレクトボックスのカーソル移動
  • レビューの評点スターhoverイベント

などが、コンポーネントに閉じたstateとして適していそうです。以下は、転職会議のUIイベントをスクショしたものです。

f:id:livesense-made:20160926162444g:plain

https://jobtalk.jp/

これはUI上の描画に関わる状態であり、Local stateとしてコンポーネント内で利用するのが良いでしょう。

一方で、ドメインやビジネスロジックに関わる状態は、ReduxでGlobal stateとして管理するのが良さそうです。

また、Local stateとして定義したものが、実はGlobal stateとして定義するべきだった…ということもあるかもしれません。状態がGlobalで持つべきものなのかLocalで持つべきものかについては、チーム内で話し合うと良いかもしれません。

締め

今回はReduxでGlobal stateとLocal stateを共存させる方法について書かせて頂きました。

Global state, Local stateと偉そうな名前を使いましたが、要はReduxと素のReactを一緒に使っているだけですね。ただし、Redux初心者の自分としてはなるほどなあという気持ちでした。

適宜Local stateを用いることで、Reduxの難点の1つであるState, Reducerの肥大化を抑制できるかもしれませんね。 React・Reduxアプリケーションを作成する上で、参考になりましたら幸いです。