- 投稿日:2020-01-20T20:44:11+09:00
React hooksとclassコンポーネントにおけるsetStateの挙動の違いについて(動かせるコードあり)
概要
こちらは以下の記事に書いたhooksとclassコンポーネントにおけるsetStateの違いを別記事にしたものです.
https://qiita.com/yseki_/items/71511db1a60ab22ee663忙しい人向けまとめ
hooksとクラス型コンポーネントのsetStateでは挙動が違う
hooksでは古いステートが引き継がれないため,更新の際は古いステートもセットする必要がある.それぞれのステート更新について
まずはhooksとclassのsetStateの違いを体験してみてください.
以下二つをhtmlファイルに貼り付け,お好きなブラウザで開いてみてください.
うまく動作しない場合,以下のリポジトリをcloneしてファイルをブラウザに貼り付けてください.
https://github.com/YutaSeki36/reactSetStateDiffクラス型コンポーネント
classComponent.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>ClassComponent</title> <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <!-- Don't use this in production: --> <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> class ClassComponent extends React.Component { constructor(props) { super(props); this.state = { a: 0, b: 0 }; this.handleChangeA = this.handleChangeA.bind(this); } handleChangeA(a) { this.setState({ a: this.state.a + a }); } render() { console.log(`a: ${this.state.a}, b: ${this.state.b}`); return ( <div> <p>a: {this.state.a}</p> <p>b: {this.state.b}</p> <button onClick={() => this.handleChangeA(1)}>a+1</button> </div> ); } } ReactDOM.render(<ClassComponent />, document.getElementById("root")); </script> </body> </html>関数型コンポーネント
FunctionComponent.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>FuntionComponent</title> <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <!-- Don't use this in production: --> <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> const FuncComponent = props => { const [state, setState] = React.useState({ a: 0, b: 0 }); const handleChangeA = a => { setState({ a: state.a + a }); }; console.log(`a: ${state.a}, b: ${state.b}`); return ( <div> <p>a: {state.a}</p> <p>b: {state.b}</p> <button onClick={() => handleChangeA(1)}>a+1</button> </div> ); }; ReactDOM.render(<FuncComponent />, document.getElementById("root")); </script> </body> </html>検証
それぞれ見た目は以下のようになるかと思います.
まずはクラス型コンポーネントの
a+1
を押してみましょう.
このように,無事更新ができました.
次は関数型コンポーネントで動作確認を行なってみましょう.
このように,hooksでステートの更新を行うと,bがundefinedになってしまいました.
解決策
公式チュートリアルに,以下のような記述があります.
しかしクラスでの this.setState とは異なり、state 変数の更新は、マージではなく必ず古い値を置換します。
とありますので,hooksによるステート更新の際は古いステートもセットする必要があります.
先ほどの関数型コンポーネントファイルのステート更新mesoddo部分を以下のように書き換えます.
funcCom.htmlconst handleChangeA = a => { // スプレッド構文を用いて、古いステートも記述する setState({ ...state, a: state.a + a }); };結び
あまりReactに慣れていない状態でhooksに手を出してしまい,初歩的なところで躓いてしまいました.
しっかりと公式ドキュメントにも書いている内容だったため,今後新たな技術を学ぶ際は公式ドキュメントに目を通そうと思いました.(誤字脱字,間違った内容などあればコメントください?♂️)
- 投稿日:2020-01-20T18:17:37+09:00
Reactでの関数のバインド 4つの方法のメモ
はじめに
Reactでの関数のくっ付け方の方法のメモ
Bind(バインド)ってやつです目次
- renderの中でくっ付けてしまう
- renderの中でアロー関数を使う
- コンストラクタの中でくっ付ける
- クラスの属性としてアロー関数を使う
- まとめ
1. renderの中でくっ付けてしまう
onClick={this.clickHandler.bind(this)
で、そのまま行けるんですね。
ただし、これだと複雑になってくると辛そう。。。
Reactを業務で触ってないので、想像でしか無いですが。。import React, { Component } from 'react' export class EventBind extends Component { constructor(props) { super(props) this.state = { message: 'こんにちは' } } clickHandler() { this.setState({ message: 'さようなら' }) } render() { return ( <div> <button onClick={this.clickHandler.bind(this)}>挨拶</button> <p>{this.state.message}</p> </div> ) } } export default EventBind2. renderの中でアロー関数を使う
アロー関数を使用すると、こんな感じ。
onClick={() => this.clickHandler()}
import React, { Component } from 'react' export class EventBind extends Component { constructor(props) { super(props) this.state = { message: 'こんにちは' } } clickHandler() { this.setState({ message: 'さようなら' }) } render() { return ( <div> <button onClick={() => this.clickHandler()}>挨拶</button> <p>{this.state.message}</p> </div> ) } } export default EventBind3. コンストラクタの中でくっ付ける
これは、公式のドキュメントに載ってるものですね。
import React, { Component } from 'react' export class EventBind extends Component { constructor(props) { super(props) this.state = { message: 'こんにちは' } this.clickHandler = this.clickHandler.bind(this) } clickHandler() { this.setState({ message: 'さようなら' }) } render() { return ( <div> <button onClick={this.clickHandler}>挨拶</button> <p>{this.state.message}</p> </div> ) } } export default EventBind4. クラスの属性としてアロー関数を使う
アロー関数とクラスの属性を組み合わせると、こんな風にも書けるんですね。
import React, { Component } from 'react' export class EventBind extends Component { constructor(props) { super(props) this.state = { message: 'こんにちは' } } clickHandler = () =>{ this.setState({ message: 'さようなら' }) } render() { return ( <div> <button onClick={this.clickHandler}>挨拶</button> <p>{this.state.message}</p> </div> ) } } export default EventBind5. まとめ
色々な書き方がありますが、公式のが一番無難かな??
どこで付けてるのか、すぐに分かるのは大切。
最後に、まとめたのを置いとおきます。See the Pen React Function Bind by oq-Yuki-po (@oq-yuki-po) on CodePen.
- 投稿日:2020-01-20T16:55:49+09:00
WEBエンジニアへの転職を目指す外資系新卒が、8週間の学習過程を晒してみる
何をやっているのか
- 2019年3月に東京の大学(ド文系)を卒業し、続く4月に新卒として米系のITサービスプロバイダに就職
- 3ヶ月のC言語研修を経て、IBMのメインフレームやオフィスコンピュータといった汎用システムの運用・保守を行うプロジェクトに配属
なぜ転職するのか
- 就活時、IT業界を目指しており、テクノロジー、ビジネスサイド両方に興味があった
- 技術のわからないPMになりたくなかったので、最初はITエンジニアとして開発の仕事を行い、徐々にマネジメントのキャリアを歩める会社を探していた
- 日本のSIerのほとんどが最初から上流工程でほとんどプログラミングをできない、もしくは下流工程でずっとコーダーという、極端なパターンが多いことを就活の業界研究の過程で学んでいった
- 最初に開発、徐々にマネジメントというキャリアを歩める会社に絞って就活を行い、理想的な会社に就職できた(と思っていた)
- 入社後、3ヶ月の研修が終わると、汎用システムの運用保守を行うプロジェクトに配属される
- 仕事内容はマニュアル通りにコマンドを実行し、システムに問題がないか確認したり、定期監視のレポートをチェックして、問題があれば該当部門に連絡するという技術的なスキルが何も身につかないものだった
- 自分のキャリアが腐っていく音が聞こえたので、2019年11月から転職を決意して独学でWEB界隈の勉強を開始する
なぜこの記事を書くのか
- 自分が勉強を始めたとき、どういったロードマップを設定して進めていけばいいのかわからなかったので、同じような境遇の人の参考程度になればと思った
- 勉強してきたことを整理して、今後の学習方針の見直し、参考にするため
- 識者の方々から学習方針に関するアドバイスが得られればいいなあという希望的観測
どう勉強してきたか
早速本題。
基本的な自分のスタンスとして、まずは未経験の門戸が広いフロントエンドエンジニアとしての就職を目指している。実際のところはフロントもバックエンドも興味がありやりたいことが多すぎるので、とにかくまずは転職を成功させてスキルを伸ばしていった上で自分の進む分野を決めたいと思っている。
また、スクールに通うつもりはなくこれまでもすべて独学で勉強を進めてきた。スクールに行かないのはお金の理由もあるけれど、どこも胡散臭い印象が拭えないというのが一つと、大学受験のときから一人で黙々と勉強を進めるのが得意だし好きだったというのが主な理由。
勉強開始して1週間ほど経ったころからTwitterを始め、勉強した日は日報を残すようにしているので、それをベースにしながらどんなことをやってきたのか思い出しながら下記に1週間単位でまとめていく。
ちなみに現時点(2020年1月20日)で学習開始から59日目、学習総時間は272時間となっている。
1週目
HTML・CSS
HTML5+CSS3 手を動かしてマスターする WEBデザイン/プログラミング動画講座
最初に手を付けたのがUdemyの上記講座。10時間程度の講座で、1.5倍速で再生して進めていった。このコースでは、タイトルにもあるように動画に沿って実際に手を動かしながら進めたので、実質15時間ぐらいかかったと思う。
これでHTML・CSSの基礎の基礎は理解できたと思ったのでJavaScriptに移った。JavaScript
The Complete JavaScript Course 2020: Build Real Projects!
Udemy for Businnessという法人向けのサービスを会社が契約していることを知り、それを利用して見始めたのが上記のコース。会社のアカウントでは英語のコースしか利用できないものの、全て無料で登録、視聴できるのがかなり嬉しかった。このコースではJavaScriptの基本から始まり、検索結果を元にAPIを叩くようなある程度本格的なWEBアプリの開発まで体験できる。JavaScriptの基本パートではES5をまず教わり、その後ES5との違いを比較しながらES6をわかりやすく説明してくれる。
このコースのいいところは以下の点。
- Q&A機能が開放されていて、複数のスタッフによるサポート体制が整っている
- 講義→実践という流れで、学んだことを実際に使う機会が豊富に用意されているのでしっかり理解できる
- 本当に知識がゼロでも、Whyがわかる構成になっている
特に3は自分の中で大きくて、本質的な理解ができないと気がすまない自分にはありがたかった。先生のJonasはこのコース以外にも、HTML・CSS基礎、CSS上級コースなどをやっていて、自分はこの人がすごく気に入ったので後にこの2つも受講した。
これが初めての英語のコースだったが、英語の講座の充実度とコンテンツの質の高さに驚いた。英語のリスニングに問題がない人は絶対に英語の講座がオススメ。JonasのHTML・CSS講座を後に受けることになるが、最初に見たUdemyの日本語講座に比べて量も質も圧倒的に勝っていて、もう日本語の講座は受けなくていいやとなった。有料だし。
2週目
LP模写
動画を見続けることに疲れたのと、1週目にやったHTML・CSSの復習がしたくなったので、「LP模写 初心者 オススメ」とかで適当に検索して出てきた以下のサイトを模写することにした。
flower | かわいいが届く、ずっと無料のお花便目的はHTML・CSSの基本的な書き方やよく使うプロパティを覚えることだったので、完璧に模写するつもりはなかった。Google Developer Toolもガンガン使ってずるしまくったのに結局完成させるのに20時間ぐらいかかった。でも1週目の講座の学びも思い出せたのでやってよかったと思う。アウトプット本当に大切。
3週目
CSS上級
Advanced CSS and Sass: Flexbox, Grid, Animations and More!
上記のコースを開始する。JavaScriptの講座でお世話になったJonas先生のCSS上級講座。CSSの仕組みを始めとして、複雑なアニメーション、NPM、Sass、BEM設計、Flexbox、CSS Gridなどなど、幅広いながらも実際の業務では必須とも言える(実務未経験のため予想w)内容を網羅してくれている。Jonas先生の良さは言うまでもなく、実際の演習で作るWebサイトがとてもおしゃれで作っていて楽しかった。28時間のボリュームのため骨が折れるかもしれないが、つまみ食いしていく進め方でも良いと思う。自分は1.5倍速で進めたものの、さすがのボリュームに途中で疲れたので一旦視聴をやめて、いままで習ったことの総復習としてToDoアプリを作り始めた。その作成過程でやっぱりFlexboxあったほうが便利だろうなあと思い、Jonasのコースに戻ってそのパートだけ視聴したりした。実際CSS Gridのパートは見ないまま終わっている。
必要になったら学習するスタイルのメリットは3つほどあると思っていて、
- 学んだことをすぐにアウトプットする場があるので「わかる」→「できる」の転換が早い
- なぜその技術が重要なのか、必要なのか理解しやすい
- 「時間をかけて学んだのに、その知識・技術は結局使うことがなかった」という事態を避けられる
これに関してはどの3つもすごく大きなメリットだと思っている。3に関しては、とにかく一刻も早く今の会社を抜け出したい自分にとって、時間効率という観点からも非常に重要な要素だ。
一方で、網羅的な知識を身に着けることの重要さも感じているので、そのあたりは転職活動を終えて時間に余裕ができたら進めていきたいと思っている。
ともかく、フロントエンドの基礎を学びたいに人にはJonas先生おすすめです。セールのときにでもぜひ買ってみてください。
4週目
Git
Git Complete: The definitive, step-by-step guide to Git
次に模写をするときにセーブ機能が欲しくなったので、Gitの勉強を始めようと受講したのが上記のコース。ただ、これは途中で視聴をやめてしまった。というのも、Git自体は複雑なものではないので、動画で学習するより公式のドキュメントなどの文章形式のほうが時間効率的にもよろしいと思ったから。実際、Gitに関する資料は充実していて、
- サルでもわかるGit入門
- gittutorial Documentationこのへんを読んで、あとはわからないことを都度調べていくぐらいでいいのかなと思った。
LP模写
JonasのCSS上級コースで作成したWebサイトを、Google Dev Toolを最低限使用して自分でゼロから作り直す作業を始めた。
目的としては、
- CSS上級の内容を復習したかった
- Gitを使ったバージョン管理を試してみたかった
という2つだったので、こちらも詰まったらすぐDev Toolでズルしつつサクサクと進めていった。
ToDoアプリ作り
先に少し話した、ToDoアプリの作成を開始した。 この目的は3つで、
- CSS上級コースの視聴(インプット)に疲れて、逃げ場としてアウトプットしたくなった
- JavaScriptの内容を忘れている気がしたので復習したかった
- そろそろ一つぐらいまともな成果物を作りたかった
このToDoアプリ作りは割と正解だったと思っている。必要なことを必要なタイミングで学ぶことの有用さや、Gitの便利さ、HTML・CSS・JavaScriptの理解の深化といったことはもちろん、ゼロから自分の手を動かしてモノを一つ作ったという達成感と自信を得られたのは良かった。その後の学習の大きなモチベーションにもなったと思う。
5週目
Chingu事前課題作成
Chinguというサービスを発見する。これは、世界中の人とリモートでチームを組んで、チームで設定した成果物を一緒に進めていくことができるサービスだ。本開発への参加の前に事前課題の提出が必要なため、こちらに取り組んでいた。事前課題をもとに、運営が適切なレベルのチームに振り分けてくれるらしい。
自分は一番下のコースであるHTML・CSS・JavaScriptのチーム開発をしたかったので、それに対応する事前課題を選択してLP模写を行った。LP自体は完成したものの、結局Chinguの本開発には参加しなかった。というのも、本開発に参加するとその作業に時間をかなりとられることになり、他の作業に時間を割けなくなりそうだと途中で思ってしまったから(登録する前に気付けw)。これに割かなければいけない時間に対して、オンラインでチーム開発をしたという経験は、果たして転職活動においてどれほど有利になるだろうかと考えた結果、そこまで優先順位は高くないと考えた。
この事前課題に使ってしまった時間は今思うともったいなかったなと。Chinguというサービスを見つけて「このサービスおもしれー!」みたいな勢いで始めてしまったので、何事も作業に取り掛かる前に時間対効果を熟慮することが大切だと感じた。当たり前の話だけど、時間がないなら作業の優先順位を厳格につけなければならない…。
React
次にJSフレームワークを学習しようと思い調べてみると、Angular, React, Vueの3つが現在の主流であることを知った。悩みに悩んだ結果、大学時代の友人がReactでサービスをなにか作ろうと思っているという話を聞いて、自分も勉強ついでに手伝えればと思いReactを選択した。結局その話はなくなったので意味はなかったのだけどw
使用した教材はこれ。
React - The Complete Guide (incl Hooks, React Router, Redux)先生のMaxがこれまたいい。表情が豊か、ジェスチャーも豊か。テンションがポジティブで聞いているこちらも上向きな気持ちで勉強を進められる。動画の総時間は驚異の45.5時間。かなりのボリュームなので全て一度でマスターするのは不可能。自分は例にも漏れず必要になったタイミングで必要なパートを視聴するスタイルで進めた。
自分の場合、ReactとRailsで転職活動に使うポートフォリオを作るつもりだったので、まずはReactの基礎は知らないとアプリを作り始められないのでReduxの前のパートまでは一気に進めていった。
6週目
神経衰弱ゲーム作成
引き続きMaxの動画を見てReactの学習を進める。結局Reduxのパートまで動画を見進めて、アウトプットがしたくなったので神経衰弱ゲームを作成することにした。が、これは途中でやめることになる。
このころ、2020年の4月中に転職活動を終えるため、いつまでに何を終わらせればいいのか逆算をしていた。その結果、このままのペースではスケジュールに的に間に合わないことがわかってしまったのだ。1月中にポートフォリオのフロント部分(View)完成とRails基礎、2月にはRails TutorialとAWS、Dockerの勉強、3月にはポートフォリオのバック部分(Model, Controller)完成、4月に転職活動という流れだ。
そのため、神経衰弱ゲーム作成は早々に切り上げて、Reactのインプットと並行して転職活動で使うポートフォリオ作りに入ることにした。
7週目
ポートフォリオ作成
どうせ作るなら自分が使いたいと思えるものを作りたかったので何を作るか丸1日考えた。自分は小さい頃からゲームが大好きだったので、ゲームをテーマとして、ゲームプレイの感想を記録できるサービスを作ることにした。ただ、長々とした感想を書かなければいけないとなると大仰だし億劫になってあまり使わなそうだったので記録できる項目を絞ることにし、
- 星5点満点での評価
- 良い点を3行で書く
- 悪い点を3行で書く
という3つに限定して、ユーザー(自分)が使用するハードルを下げてみた。
アイデアが固まったらプロトタイプ作成もしてみたかったので、Justinmindというプロトタイプ作成アプリ(無料)で画面のレイアウトをざっくりと作ってみたりした。
こういう作業を年末年始にしていた。年明けからは本格的な実装に入ってみたものの、やっぱりReactの書き方がわからんすぎてMaxの講座で作ったサービスのコードを真似しながら少しずつ進めていった、というか現在進行系で進めている。
8週目 〜 現在
Ruby + Rails
ポートフォリオ作成は変わらず進めていたが、並行してバックエンドの技術として使うRailsの勉強を始めた。最初はUdemyの動画で学習を進めようと思っていたが、一番レビューが多い講座の内容が冗長な気がしたのでやめた。
結局Progateに登録して、基礎を固めることにした。Progateは評判の通り本当に素晴らしくて、楽しくサクサクと進めることができた。最初見ていたUdemyの講座内容に比べると、内容が端折られている気はしたが、プログラミング学習のきっかけを提供するというProgateの理念を考えると仕方がないと思った。網羅的な学習はポートフォリオ完成後に余裕ができてから行うつもり。
今はRubyとRailsのコースが全て終わったところで、次はRails Tutorialに着手する予定。もし難しすぎたらUdemyの動画を挟んでからリベンジしようと思っている。
Jest + Enzyme
ポートフォリオの成果物だけでなく、設計やテストなどの思想・周辺技術をどれほど考慮できているかということが重要という情報をツイッターで見かけた。なるほど確かにテストは実際の業務で必ず必要になってくるなと思い、ポートフォリオ作りの際にもテストを行っていこうと考えた。現在はUdemyで2019 Update! React Testing with Jest and Enzymeというコースを受講している。
そのため、一旦ポートフォリオ作成の作業は止めている。このコースでテストが何たるかというのを理解したら、ポートフォリオ作成を再開して、TDDを試してみようと思っている。
さいごに
今後の目標
1月の目標は、ポートフォリオのフロント側を完成させること、ProgateのRuby + Railsコースを終わらせることだったので後者は達成している。正直あと10日程度で前者を達成する自信はないが、引き続きストイックに作業を進めていってできるだけ2月に作業がずれ込まないようにしたい。
2月はRails Tutorialの終了とAWS、DockerのUdemy講座各1つずつ見る。
あと全体目標として、Qiita投稿を月に4回を設定してあるので今月はあと2記事書かなければwアウトプットが何かしら自分の糧になると信じて頑張っていこう。
問い合わせ
この記事に関してでもそれ以外のことでも、もし何かお聞きしたいことなどありましたらTwitter(@Ryo_Code)でもこちらでもお気軽にコメントください。自分の分かる範囲でお答えさせていただきます。
- 投稿日:2020-01-20T16:03:00+09:00
styled-componentsのas propとforwardedAsについて整理してみた
はじめに
styled-componentsの
as prop
とforwardedAs
への理解がふわっとしていたため、この機会に整理してみた。
特にforwardedAs
は本家サイトを見ても理解しにくかったので、、、今回ちゃんと整理した。as propってなに?
v4から追加された機能で、レンダリングする要素のタグを動的に変更できる機能。
styled-componentsでスタイルは使い回して、レンダリングされるタグだけ変更したい場合に利用する。「ボタンとリンクを別で実装するが、同じスタイルを適用したい」などのユースケースで非常に役立つ。
同じスタイルのコンポーネントを作り分けなくてよくなるのはありがたい。as propはどうやって書く?
ざっくり書くとこんなかんじ
Hoge = styled.div``; // .Hoge(div) Piyo = styled(Hoge)``; // .Piyo(Hoge + Piyo)(div) <Piyo as="h1" /> // .Piyo(Hoge + Piyo)(h1) Hoge = styled.div``; // .Hoge(div) Piyo = () => <Hoge />; // .Piyo(() => <Hoge /> + Piyo)(div) <Piyo as="h1" /> // .Piyo(() => <Hoge /> + Piyo)(h1)具体的に書くとこんなかんじ
import styled from "styled-components" import { Link } from "react-router-dom" // divなど、汎用的なタグで定義しておくと使い回しやすいかも。 const Base = styled.div` color: red; `; // divのまま使ってもOK <Base>Hello World!</Base> // spanとして定義 <Base as="span">hoge</Base> // linkとして定義 <Base as={Link} to="home">hoge</Base> // 継承も可能 const FontBold = styled(Base)` font-weight: bold; `; <FontBold as="span">hoge</FontBold> <FontBold as={Link} to="home">hoge</FontBold> // 継承はこんな書き方もできる const FontLarge = styled(() => <Base />)` font-size: large; `;forwardedAsってなに?
v4.3から追加された機能で、
styled()
にas prop
を渡す場合に利用する機能。
別コンポーネントをラップする場合、forwardedAs
を使用してラップされたコンポーネントにas prop
を渡すことが可能。
※拡張スタイル付きコンポーネントではas prop
を使用しても親スタイルは継承されない。forwardedAsはどうやって書く?
import styled from "styled-components" const Cell = styled.td` background-color: ${props => props.bgColor || "blue"}; `; const StyledCell = styled(Cell)` color: red; `; const SomeCell = ({ as, ...props}) => { return <StyledCell forwardedAs={as} {...props} />; }; const ExtendedCell = styled(SomeCell)` border: 1px solid red; `; render( <table> <tr> <ExtendedCell bgColor="orange" forwardedAs="th"> hoge </ExtendedCell> </tr> </table> );まとめ
モヤモヤが少しスッキリした気がする。
あと、複雑なコードを極力書かないで済むような設計にしたいと改めて思った。
- 投稿日:2020-01-20T13:18:42+09:00
ReduxにおけるImmutableの概念についてまとめてみた
はじめに
ReactやReduxを触っていると、多くの人は、Immutable、mutableというワードを耳にするのではないでしょうか。もちろんこれらは他の言語やフレームワークを使用するにあたっても重要とは思いますが、React、Reduxで開発するにあたってはとりわけ重要な概念です。実際、Reduxのstyleガイドで挙げられているReduxによるコーディングの原則の一番初めにデカデカと書かれているのが、mutableにデータを変更してはならないという指摘です。それくらい重要な概念であるImmutableですが、ちょっと理解が難しく、また理解しなくても一通り開発が進めらてしまうため(もちろんそのうち壁にぶち当たりますが)、言われてもあまりピンとこないのではないでしょうか?(筆者自身もそうでした)。この記事では、その概念について、利点や、実際のコーディング例も交えながらわかりやすく解説していきたいと思います。これを最後まで読めば、あなたもImmutableについての理解が深まると思います。
Immutableコーディングの利点
Immutableなコーディングには複数のメリットがあります。
1. シンプルで可読性の高いコーディングが可能になる
2. デバックしやすい
3. React等のDOM動作のフレームワークが正確に動作するなぜReduxにおいてImmutableなコーディングが必要なのか
- Redux、React-Reduxはともにshallow equality checkingを採用しています。ReduxのcombineReducersでは、そのcombineReducersが呼び出したreducerの変化をshallow checkingしている。React-Reduxのconnectメソッドでは、RootStateの変化をshallow checkingして、コンポーネントが再renderされるかを判別しています。
- Immutableなコーディングでは、より安全にデータを扱うことができるようになります。
- 副作用のない関数によってreducerが定義されることにより、デバックの際、別個のstate間を正しく追跡することが可能になります。
そもそもshallow equality checkingとは?deep equality checkingとの違い
shallow equality checking => 2つの異なる変数が同じ参照元(データの保存先のメモリの番地)かどうかをみているのみです。そのため、実際のデータの中身のみが変化しても、そのデータの参照元が同じである場合、変更が検知されません。
deep equality checking => 2つの異なる変数の実際のデータの値が同一のものであるかどうかをみています。Reduxでは、そのパフォーマンスの向上の理由上からshallow equality checkingが採用されています。
具体例1
Reduxのstateを変更する際。
悪いコード例)const exampleReducer = reducerWithInitailState(initialState) .case(actions.exampleAction, (state, payload) => ( state.push(payload) ))良いコード例)
const exampleReducer = reducerWithInitailState(initialState) .case(actions.exampleAction, (state, payload) => ( state.concat(payload) ))上記の2つの例の違いは、元のstate(ここでは配列を想定)にどのように引数を追加しているかという点です。1つ目の例で使われているpushメソッドは、配列に対して要素を追加する際、元の配列を直接書き換えて、変更します。そのため、その配列の参照元(データの保存先のメモリの番地)が変わることはありません。
一方で、2つ目の例にあるconcatメソッドは、元の配列に影響を与えず、結合された新たなオブジェクトを生み出します。そのため、元の配列を複製した上で新たな配列(結合された新しい配列)を生成するため、元のstateの配列と新しい配列ではその参照元が異なります。
具体例2
悪いコード例)
const initialstate = { name: "Rui", age: 22, address: "多摩" } const exampleReducer = reducerWithInitialState(initialState) .case(action.exampleAction, (state, payload) => ( state.address = "六本木" ))良いコード例)
const initialstate = { name: "Rui", age: 22, address: "多摩" } const exampleReducer = reducerWithInitialState(initialState) .case(action.exampleAction, (state, payload) => ( ...state, address: "六本木" ))悪いコード例では、stateのaddressを直接書き換えていますが、良いコード例では、stateを一度複製し、複製したstateに対して変更を加えているため変更前のstateが書き換わっていません。
おわりに
ReduxおよびReact-Reduxにおいては、shallow equality checkingが採用されているためstate等のデータを直接書き換えるようなImmutableでないコーディングを行ってしまうと、その変更が検知されず、stateの変更があったにも関わらず、componentが正しく再renderされないなどの事態が生じる恐れがあります。また、はじめの方でも書いたように、Immutableなコーディングはデバックの際にもたいへん有効ですし、コード全体の可読性も上がるため、チーム開発の際にもとてもメリットがあります。気にかけていないと、ついうっかりmutableなコーディングをしてしまいがちなので、ぜひ注意してくださいね! Reduxの公式FAQ(下にもリンクを貼っておきます)はかなり詳細にこの概念について書かれていて、とても勉強ななるので、ぜひ一読してみてください。
参考にしたサイト
- 投稿日:2020-01-20T12:11:57+09:00
styled-componentsのコンポーネントはReactのrenderメソッドもしくはFunctionalコンポーネントの中で実装しないようにしよう
ある日以下のような実装でtextareaのvalueをuseStateで管理しようとしたときに、textareaに1文字を入力するとtextareaからフォーカスが外れるということが起こりました。
import React, { useState } from "react"; import styled from "styled-components"; export default function App() { const [value, setValue] = useState(""); const handleChange = event => { setValue(event.target.value); }; const StyledTextarea = styled.textarea` width: 100%; `; return <StyledTextarea value={value} onChange={handleChange} />; }こんな感じ。
https://codesandbox.io/s/icy-hill-5k1hn?fontsize=14&hidenavigation=1&theme=dark解決方法
- https://github.com/styled-components/styled-components/issues/2925
- https://styled-components.com/docs/faqs#why-should-i-avoid-declaring-styled-components-in-the-render-method
styled-componentsのコンポーネントをrenderメソッドもしくはFunctionalコンポーネントの中で実装すると、renderメソッドが呼ばれるたびにstyled-componentsのコンポーネントが再作成されるので、パフォーマンスの問題と意図しない挙動が起こる可能性があるようです。
NG
const Header = () => { const Title = styled.h1` font-size: 10px; ` return ( <div> <Title /> </div> ) }OK
const Title = styled.h1` font-size: 10px; ` const Header = () => { return ( <div> <Title /> </div> ) }
- 投稿日:2020-01-20T02:07:13+09:00
React Native体験入学【環境構築】【標準コンポーネントの使い方】【リリースまで】
最近までNext.jsの入門してブログシステム作ってたけど、どうもwebpack周りで詰んでしまったのでもう少し実戦練習できることをやってみようと思ってReact Nativeを始めることに。あまりNext.jsに向いてないブログシステム作るより、ガッツリReact使いまくるアプリ制作でコーディングしまくりたい。
とりあえずReact Nativeの基本情報と、環境構築、ドキュメントやリファレンスの流し読み、そして簡単なアプリを作ってみようと思う。予定は簡単な「ダイス」アプリ。できればリリースするところまで経験しておきたい。
目次(暫定)
- 環境構築
- React Nativeとは(クロスプラットフォーム開発環境)
- Reactとは(UI構築のためのJSライブラリ)
- Expoとは(OSに依存しないビルド環境)
- 統合開発環境
- Xcode
- Android Studio
- 開発ツールのインストール
- Homebrew
- Node.js
- Watchman
- react-native-cli
- 動作テスト
- 仕様
- 標準コンポーンネント
- 標準API
- StyleSheet
- HTTP通信
- アニメーション
- データの保持
- 画面遷移(navigation・pagenation)
- 標準UI(React Native Elements)
- ビルド・リリース
- Google Play
- AppStore
- デバッグとアップデート
React Nativeとは
- Facebookが開発したクロスプラットフォーム開発フレームワーク
- FlutterみたいなやつのReact版
- 同じコードでWeb・iOS・Androidアプリが作れる
- OSのAPIの仕様の差をフレームワークレベルで抽象化することで共通化
- JavaScriptとReactの知識があればモバイルアプリを作れる
Reactとは
UI構築のためのJSライブラリ。コンポーネントは状態を持ったviewの単位。Reactではコンポーネントを組み合わせてUIを構築する。その辺はReact.jsの方の公式ドキュメントなどで勉強すべし。
コンポーネントライフサイクル
これはいずれ理解しないといけないややこしい部分。数が多いのでまた今度気合入れて勉強しとく。
ReactDOM.render
constructor()
getInitialState()
非推奨。constructor()推奨。componentDidMount()
componentWillUnmount()
shouldComponentUpdate()
getSnapshotBeforeUpdate()
componentDidUpdate()
componentDidCatch()
Expoとは
- OSに依存しないビルド環境
- スマホ用の開発環境アプリがある
- 同じネットワークなら無線接続でテスト可能
- 利用には会員登録が必要
Expo Snack
ブラウザ上でコードの動作をリアルタイムで確認できるツール。ReactのCodePenやCodeSandboxのようなもの。
アプリの実行方法は2つ
実機で動かすのと、シミュレータで動かすもの。
実機で動かすときは、Expo.ioからプロジェクトを開始したら表示されるQRコードを、スマホのExpoアプリで読み取れば起動する。
シミュレータで動かすときは、
expo-cli
というコマンドラインツールをPCにnpmインストールして、expo init
コマンドを実行することでExpoプロジェクトに接続可能。ただしreact-native init
コマンドとは異なることに注意。コマンドラインからでも
expo start
コマンドでQRコードから実機接続できる。また、このときにi
を入力すればシミュレータが起動する。統合開発環境
実機を持ってなくとも新旧機種を問わず動作テストできる環境を構築する。それにはXcodeとAndroid Studioといった統合開発環境をインストールすればいい。ただしiOSアプリの開発はMacOSでしかできない。以下、必要なツールをリストアップしておく。
Xcode
Android Studio
- Java(開発言語)
- JDK(Java開発キット)
- SDK(ソフトウェア開発キット)
- AVD(Android仮想デバイス)
その他
- Homebrew(パッケージマネージャ)
- Node.js(非同期のJSライブラリ)
- Watchman(ファイルシステムの監視ツール)
- react-native-cli(コマンドラインツール)
- Expo
動作テスト
公式ドキュメントを参考に一通りやってみる。
各種インストール
環境はMacOS & iOSを選択。そしてXcodeをインストールすることでHomebrewが使えるようになる。
$ brew install node $ brew install watchman $ sudo gem install cocoapods$ npx react-native init MyTestApp
$ npm install -g react-native-cli $ react-native init MyTestAppシミュレータ起動して試運転
アプリをシミュレータで起動するコマンド。
$ cd MyTestApp $ npx react-native run-ios
App.js
を編集して保存したらホットリロードされる。次に公式ドキュメントのHello Worldコードを
App.js
にコピペして保存してみる。ここからは普通のReactのコーディングだと思えばわかりやすい。App.jsimport React, { Component } from 'react'; import { Text, View } from 'react-native'; // react-native標準コンポーネントのインポート export default class HelloWorldApp extends Component { render() { return ( <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}> <Text>Hello, world!</Text> </View> ); } }ちょっとだけ自由に編集してみた。まずrootディレクトリに
public/
を作成し、profile.png
を配置。そしてApp.js
でImage
コンポーネントをインポートのところに追加し、render()
の中にpropsやstyleを与えた<Image />
を記述。ついでに<Text>
のフォントサイズのstyle追加とテキストを変更してみた。レイアウトは<View>
コンポーネントのstyleでセンタリングされて表示される。import React, { Component } from 'react'; import { Text, View, Image } from 'react-native'; export default class HelloWorldApp extends Component { render() { return ( <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}> <Text style={{fontSize:30}}>お絵描きホーホー論</Text> <Image style={{width: 100, height: 100}} source={require('./public/profile.png')} /> </View> ); } }標準コンポーネント
React Nativeのコンポーネントの構造についてサッと頭に入れておくと知識を蓄積しやすくなるかも。下図は公式サイトのトップページにある図解。React Nativeではいくつかの標準コンポーネントを提供しており、それをツリー構造で配置することでUIを構築し、そのReactコンポーネントはネイティブなコードをラップしているため、ネイティブコードを一切触らずにJavaScriptでのアプリ開発ができる、という触れ込みが書かれている。
標準コンポーネントの種類については、公式ドキュメントのコンポーネントとAPIのリファレンスを参考。基本的な説明と、渡せるpropsの一覧が見れる。名称から用途が想像できるものもあるが、各プラットフォーム専用のコンポーネントもあるので要勉強(下記が全てではない)。
- 基本コンポーネント
- View
- Text
- Image
- TextInput
- ScrollView
- StyleSheet
- UI
- Button
- Picker
- Slider
- Switch
- List Views
- iOS Components and APIs
- Android Components and APIs
- Others
- Activity Indicator
- Alert
Viewコンポーネント
HTMLでいう
<div>
のような役割。要素を入れるコンテナとして、レイアウトを作るのに使える。return( <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}> (中略) </View> );Textコンポーネント
テキスト表示用で、HTMLと違ってReact Nativeではこのコンポーネントを使わないとテキスト表示ができない。
return( <Text>Hello World</Text> );Imageコンポーネント
画像を表示する。画像ファイルパスは
require
でpublic/
などの静的ファイル用のディレクトリを参照するようになる。Imageコンポーネントはstyle
でサイズ指定しなけれな表示されない。基本的には位置調整のため<View>
コンポーネントに入れて配置する。return( <Image style={{width: 100, height: 100}} source={require('./assets/sample.png')} /> <Image style={{width: 100, height: 100}} source={{uri: 'https://facebook.github.io/reactnative/img/favicon.png'}} /> );Native Baseの導入してUI構築
Native BaseはReact NativeにUIコンポーネントを追加するライブラリ。そもそもReact Nativeはクロスプラットフォーム開発環境を提供することに重点を置いているので、OSごとに仕様が異なるUI設計などについては最低限のサポートしかしてくれない。例えば
<Button>
の標準コンポーネントだけだとiOSではボタンではなくただのテキストとしてしか表示されない。npmでインストールしてから
link
を実行することで有効にする。$ npm i -S native-base $ react-native link使い方は
react-native
の代わりにnative-base
としてコンポーネントをインポートすればいい。App.jsximport React, { Component } from 'react'; import { Container, Button, Text } from 'native-base'; export default class GeneralExample extends Component { render() { return ( <Container> <Button> <Text> Button </Text> </Button> </Container> ); } }UIライブラリ
React Native Basic Components
React Native Elements
Native Base
IonIcons