2022年4月28日 アルゴリズムを自作して問題を解く
問題
1以上の整数nが与えられたとき、1からnまでの整数の内 7で割り切れない数の合計を出力するプログラムを示してください。
例えばnが7の場合は1, 2, 3, 4, 6, 7の合計を出力する。
関数型プログラミングと手続型プログラミングの違い
関数型プログラミング
過程ではなく、結果に重きを置いている。何をしたいかを書くだけ
手続型プログラミング
結果を得るための過程に重きを置いている
関数型は過程をメソッドに入れて、過程を抽象化している
手続型で解く
手続き型の場合、以下のようになりました。
const testNum = (n) => { let totalNum = 0; for (let i = 0; i <= n ; i++) { i % 7 !== 0 ? totalNum += i : totalNum - 7; } return totalNum; } testNum(7)
▼出力結果 15
手続き型の場合、何をやっているのか読み解きづらいです。
関数型で解く
https://paiza.io/projects/L5DLuSgZAyNYZUADmjXsOg?language=ruby
const testNum = (n) => { const num = [...Array(n).keys()].map(i => ++i) .filter(n => n % 7 !== 0) .reduce((accumulator, currentValue) => { return accumulator + currentValue; }, 0); return num; } console.log(testNum(5));
▼出力結果 15
- ...Array(n)では引数に渡された数字の数だけundefinedが要素として生成されます。
- [undefined, undefined,undefined,undefined, undefined,undefined, undefined]
- 配列.keys()でインデックス番号の数字の配列を生成します。
- 7の場合[0, 1, 2, 3, 4, 5, 6]となります。
- 配列を1から始めたいため、mapで要素に1をプラスしています。
- [1, 2, 3, 4, 5, 6, 7]となります。
- 7で割り切れる数を省きたいため、配列のfilterメソッドを使用します。
- [1, 2, 3, 4, 5, 6]となります。
- あとは配列内の数値を足すだけです。reduceメソッドを使用します。
[...Array(3).keys()]のコード例
// undefinedを要素とする配列を、生成する const array = [...Array(3)]; console.log(array); // => [ undefined, undefined, undefined ]
// Array(3).keys()の結果をスプレッド構文で展開する const array = [...Array(3).keys()]; console.log(array); // => [ 0, 1, 2 ]
TypeScriptで書く場合
const testNum = (n: number): number => { const num = [...Array(n).keys()].map(i => ++i) .filter(n => n % 7 !== 0) .reduce((accumulator, currentValue) => { return accumulator + currentValue; }, 0); return num; }; console.log(testNum(7));
type TestNum = (n: number) => number const testNum: TestNum = (n) => { const num = [...Array(n).keys()].map(i => ++i) .filter(n => n % 5 !== 0) .reduce((accumulator, currentValue) => { return accumulator + currentValue; }, 0); return num; } console.log(testNum(5));
2022年4月26日 りあクト! 第6章 ESLintの適用ルールをカスタマイズする (p.54~)
依存パッケージをリストアップする
npm info パッケージ名 peerDependencies
を実行すると、依存しているパッケージをリストアップできます。
以下の例では、styled-componentsの依存パッケージをリストアップしています。
➜ frontend git:(feature/add_#403) npm info styled-components peerDependencies { react: '>= 16.8.0', 'react-dom': '>= 16.8.0', 'react-is': '>= 16.8.0' }
.eslintrc.jsをカスタマイズ
module.exports = { env: { browser: true, es2021: true, }, extends: [ // 'plugin:react/recommended', // ESLint の組み込みルールに対する公式推奨の共有設定 'airbnb', // 上のrecommendedと順番が前後すると意図しないところで TypeScript のコードに ESLint がエラーを指摘したりするようになる 'airbnb/hooks', 'plugin:import/errors', 'plugin:import/warnings', 'plugin:import/typescript', 'plugin:@typescript-eslint/recommended', // eslint:recommended から TypeScript の一般的な文法とバッティングするルールを 調整するための共有設定 'plugin:@typescript-eslint/recommended-requiring-type-checking', ], parser: '@typescript-eslint/parser', parserOptions: { // parserである@typescript-eslint/parser へ渡すオプションを定義します。 ecmaFeatures: { jsx: true, }, ecmaVersion: 12, project: './tsconfig.eslint.json', // 別ファイルを用意することでパーサが npm パッケージのファイルまでパースしてしまって、VS Code と連携させた際のパフォーマンスを落としたり、新規ファイルのパースの失敗を防ぎます。 sourceType: 'module', tsconfigRootDir: __dirname, }, plugins: [ '@typescript-eslint', 'import', 'jsx-a11y', 'react', 'react-hooks', ], root: true, rules: { // occur error in `import React from 'react'` with react-scripts 4.0.1 'no-use-before-define': 'off', '@typescript-eslint/no-use-before-define': [ 'error', ], 'lines-between-class-members': [ 'error', 'always', { exceptAfterSingleLine: true, }, ], 'no-void': [ 'error', { allowAsStatement: true, }, ], 'padding-line-between-statements': [ 'error', { blankLine: 'always', prev: '*', next: 'return', }, ], '@typescript-eslint/no-unused-vars': [ 'error', { 'vars': 'all', 'args': 'after-used', 'argsIgnorePattern': '_', 'ignoreRestSiblings': false, 'varsIgnorePattern': '_', }, ], 'import/extensions': [ 'error', 'ignorePackages', { js: 'never', jsx: 'never', ts: 'never', tsx: 'never', }, ], 'react/jsx-filename-extension': [ 'error', { extensions: ['.jsx', '.tsx'], }, ], 'react/jsx-props-no-spreading': [ 'error', { html: 'enforce', custom: 'enforce', explicitSpread: 'ignore', }, ], 'react/react-in-jsx-scope': 'off', }, overrides: [ { 'files': ['*.tsx'], 'rules': { 'react/prop-types': 'off', }, }, ], settings: { 'import/resolver': { node: { paths: ['src'], }, }, }, };
- extendsに各プラグインルールの推奨の共有設定が記述してあります。
- ESLintプラグインはインストールしただけでは読み込まれないため、extendsに設定することで使用可能になります。
- 共有設定の順番には意味があります。共有設定間で設定ルールの値が衝突した場合、後に記述されたものが先に記述されたものを上書きする仕様になっています。
- 依存関係の順番がある場合はドキュメントに書かれているはずなのでプラグイン・拡張ルールセットをインストールするときは、チェックしましょう。
parserのオプションで指定したtsconfig.eslint.json
別ファイルを用意することでパーサが npm パッケージのファイルまでパースしてしまうことによる、VSCodeと連携時のパフォーマンスの低下や、新規ファイルのパースの失敗を防ぎます。
extends内のprojectの記述で別ファイルを読み込む設定が行われています。
parser: '@typescript-eslint/parser', parserOptions: { // parserである@typescript-eslint/parser へ渡すオプションを定義します。 ecmaFeatures: { jsx: true, }, ecmaVersion: 12, project: './tsconfig.eslint.json', // 別ファイルを用意することでパーサが npm パッケージのファイルまでパースしてしまって、VS Code と連携させた際のパフォーマンスを落としたり、新規ファイルのパースの失敗を防ぎます。 sourceType: 'module', tsconfigRootDir: __dirname, },
tsconfig.eslint.json
{ "extends": "./tsconfig.json", "include": [ "src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx" ], "exclude": [ "node_modules" ] }
pluginsの設定
plugins: [ '@typescript-eslint', 'import', 'jsx-a11y', 'react', 'react-hooks', ], root: true,
- 読み込ませる追加ルールのプラグインです。
- インストールしただけでは使えず、上記のように設定する事で使用可能になります。
- rootオプションは、ESlintはデフォルトの挙動では親ディレクトリの設定ファイルまで読み込んでしまいます。trueにする事で防ぐことができます。(個々のケースで変更するべき)
VS Codeの拡張機能でコーディング中にESLintが走るように設定する
拡張機能 : ESLint
https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint
メニューの Code > Preferences > Settings で開いたタブグループ右横のアイコン『Open Settings (JSON)』から設定ファイル settings.json を開いて次の内容を追加
"editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "editor.formatOnSave": false, "eslint.packageManager": "yarn", "typescript.enablePromptUseWorkspaceTsdk": true
- ファイル保存時にESLintの自動整形が走るようにする設定です。
- 最後の行はプロジェクトがTypeScriptを仕様している場合、VSCodeの内蔵のLintを使用するか、プロジェクトのLintを使用するかの設定です。
.eslintignoreファイル
lintチェック対象外となるファイルを定義できるファイルです。
build/ public/ **/converage/ **/node_modules/ **/*.min.js *.config.js .*lintrc.js
package.jsonでnpmのスクリプトを追加
後半の三行を追加します。
"scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", "lint:fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'", "preinstall": "typesync || :" },
typesyncとは
package.jsonを見て足りない型定義パッケージがあれば自動で追加してくれるパッケージです。
このプロジェクトではtypesyncがインストールされており、パッケージをインストールする際にpreinstallフックで自動的にtypesyncが走るように設定しています。
typesync || : スクリプト実行時にtypesyncがインストールされていない場合に何もしないようにすることでエラーを防いでいます。
ある種のスクリプト名は特別です。定義されている場合、preinstallスクリプトはパッケージがインストールされる前にyarnによって呼び出されます。互換性の理由から、install、postinstall、prepublish、prepareと呼ばれるスクリプトはすべて、パッケージのインストールが完了した後に呼び出されることになります。 スタートスクリプトの値のデフォルトは、node server.jsです。
- yarn add {package} で任意のパッケージをインストール
- yarn add コマンドの前に必ず実行される特別なスクリプト名のpreinstallにtypesyncを設定する事で、パッケージをインストールする事と同時に型定義ファイルもインストールできるようになります。
- 初回のyarn installコマンド実行時にtypesyncはインストールされていない事で起こるエラーを防ぐために || : を付与しています。
参考
2022年4月25日 りあクト! 第6章 Linter とフォーマッタでコード美人に (p.45~)
RubyではRubocopなどを用いることでコードのバグの警告や自動整形を行うことが出来ます。対してJavaScriptではlinterやコードフォーマッタを使用します。
linterとは
コードを静的解析してコンパイルではじかれない潜在的なバグを警告するものです。
放っておくと故障の原因となる糸くずを片っぱしから絡めとる乾燥機の『lint trap (糸くずフィルター)』に似ていることから 各種言語で書かれたコードを解析して構文チェックを行うことを 『lint』と動詞化して表現されるようになり、さらにそのプログラムは『linter』と呼ばれるようになったそうです。
コードフォーマッタとは
インデントや改行などのスタイルを一律に自動整形してくれるものです。
ESlint
開発者が独自のlintルールを作れるようにすること、という動機で創られたlinterです。 拡張性に加え、充実したドキュメント、JSHint よりも読みやすくかつ柔軟に記述できる設定ファイル、出力はメッセージとともに抵触したルール の ID が表示されてしっかり根拠が説明されるという親切設計から、ユーザー数を急激に伸びていきました。
2016年4月にJSCSという別のツールの機能を取り入れることでさらにシェアが拡大しました。
JSCS(JavaScript Code Style checker)
コードスタイルをチェックすることや、チェックに引っかかった箇所を強制的に修正するコードフォーマッタの機能を備えたツールです。
TSlint
2019年1月にMicrosoftのTypeScript開発チームがそれまで主力であったTSlintをあらゆる問題があることから見限り、ESlintのTypeScript対応プロジェクトを始動した事をきっかけに、TSlintの作者はTSlintプロジェクトを非推奨としてESlintへの段階的な移行をアナウンスしました。 現在ではESlintがダウンロード数でトップのlinterとなっています。
linterの存在
ツールの栄枯盛衰はありますが、JavaScriptやTypeScriptはlinterの存在があってはじめて言語として完成されます。
linterはモダンフロントエンド開発において必要不可欠なツールとして使用されています。
ESLint の適用ルールをカスタマイズする
Parser
ソースコードを特定の言語仕様に沿って解析してくれるライブラリ。 ESlintにはJavaScriptのパーサが組み込まれているが、標準ではTypeScriptには対応していないので、TypeScriptのパーサを導入する。
Plugin
ESLintの組み込みルール以外に独自のルールを追加するもの。 それらを適用した推奨の共有設定とパッケージングして提供されることが多い。
共有設定(Shareable Config)
複数のルールの適用をまとめて設定するもの。 ESlintに同梱されるeslint:recommendedやAirbnb社が提供しているeslint-config-airbnbが有名。
設定例 (.esluntrc)
module.exports = { env: { // プログラムの実行環境をESlintに伝える browser: true, es2021: true, }, extends: [ // 複数のルールをまとめる共有設定のオプション。 'plugin:react/recommended', 'airbnb', // ESLint に同梱される eslint:recommendedやAirbnb社が提供しているeslint-config-airbnbが有名 // 共有設定間でルール設定が重複している場合、リストの後ろに記述されたほうが 優先される parser: '@typescript-eslint/parser', // ESLint が使用するパーサを指定する parserOptions: { // パーサの各種実行オプションを設定する ecmaFeatures: { jsx: true, }, ecmaVersion: 12, sourceType: 'module', }, plugins: [ // 任意のプラグインを組み込む 'react', '@typescript-eslint', ], rules: { // 適用する個々のルールと、エラーレベルや例外などその設定値を記述する }, // ルールは共有設定で読み込まれているため、個別で無効にしたり例外を設ける場合に適用する };
設定ファイルのプロパティの意味
env
プログラムの実行環境をESlintに教える。個別の環境ごとにglobalsの値がプリセットされています。
extends
共有設定を適用する。共有設定はESlintに標準で含まれているものか別途インストールしたもの、またはインストール済みプラグインのパッケージに含まれているものを指定します。 なおここに記述した共有設定間でルール設定が重複している場合、リストの後ろに記述された方が優先されます。
parser
ESlintが使用するパーサを指定します。
parserOptions
パーサの各種実行オプションを指定します。
plugins
任意(インストール済み)のプラグインを組み込みます。
rules
適用する個々のルールと、エラーレベルや例外などの設定値を記述します。
参考
2022年4月23日 りあクト! 第5章 Reactの組み込みコンポーネント(p.39~)
復習
トランスパイル
一つのプログラミング言語で書かれたプログラムのソースコードを別のプログラミング言語と同等のソースコードを生成すること また、それを行うプログラムをトランスパイラといいます。
- BabelはES2015以降のJavaScriptやJSXをトランスパイルするトランスパイラです。
- tscは、TypeScriptで書かれたソースコードをJavaScriptへとトランスパイルします。
コンパイル
人間が理解しやすい言語や数式で記述されたプログラムを機械語に変換すること。 また、それを行うプログラムをコンパイラといいます。
- tscはJavaScriptの構文チェックのみで構文ではないPromiseなどの組み込みオブジェクトは型変換出来ないため、babelと併用する必要があります。
Reactの組み込みコンポーネント
Reactのコンポーネントにはユーザー定義コンポーネントと組み込みコンポーネントの2種類があります。
ユーザー定義コンポーネントの命名規則
- 大文字から始めなければなりません。
- 小文字から始めるとJSXから組み込みコンポーネントと解釈されるため呼び出し不可です。
Reactの組み込みコンポーネントのpropsと標準のHTML要素の属性の違い
JavaScriptの挙動とかぶってしまった事で名前自体が変更されているもの
- class ⇨ className
- for ⇨ htmlFor
HTMLと挙動が異なり、値の属性がBooleanになってるもの
- checked
- disabled
- selected
value
Reactではtextareaとselectタグもvalue属性が持てます。
<form> <textarea value="Fixed Text" /> <select value="uranus"> <option value="saturn">Saturn</option> </select> </form>
小文字の名前でコンポーネントを定義してみる
小文字の名前でコンポーネントを定義すると、JSXからコンポーネントとして呼び出すことができません。JSXでは小文字から始まる名前のコンポーネントは、全て組み込みコンポーネントだと解釈されるからです。そのため、ユーザー定義コンポーネントのコンポーネント名は、必ず大文字から始めましょう。
const message = (): JSX.Element => ( <h1>ハロー</h1> ); export default function App() { return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> <message /> // => Property 'message' does not exist on type 'JSX.IntrinsicElements'.ts(2339) </div> ); }
組み込みコンポーネントだけが持つ属性
ref属性やkey属性はHTMLには存在しない組み込みコンポーネントが持つ属性です。
ref属性
コンポーネントを実際にレンダリングされるリアルDOMへ結びつける参照のための属性です。
Ref は render メソッドで作成された DOM ノードもしくは React の要素にアクセスする方法を提供します。 子要素を変更するには、新しい props でそれを再レンダーします。ただし、この一般的なデータフロー以外で、子要素を命令型のコードを使って変更する必要がある場合もあります。 公式: https://ja.reactjs.org/docs/refs-and-the-dom.html
いつ Ref を使うか
Refに適した使用例は以下の通りです。
- フォーカス、テキストの選択およびメディアの再生の管理
- アニメーションの発火
- サードパーティの DOM ライブラリとの統合
子要素を変更するには、新しい props でそれを再レンダーします。ただし、この一般的なデータフロー以外で、子要素を命令型のコードを使って変更する必要がある場合もあります。 宣言的に行えるものには ref を使用しないでください。
key属性
Reactが再レンダリングのための差分検出を効率的に行うのに必要とするものです。
Key は、どの要素が変更、追加もしくは削除されたのかを React が識別するのに役立ちます。配列内の項目に安定した識別性を与えるため、それぞれの項目に key を与えるべきです。 公式:https://ja.reactjs.org/docs/lists-and-keys.html#keys
基本ルールとしては、map() 呼び出しの中に現れる要素に key が必要です。 keyを指定しない場合はコンソールに警告が表示されます。
keyにmapの第二引数に指定するインデックスを使うのはユニークな値がない場合の最終手段です。
const Message = (): JSX.Element => ( <div> {[...Array(5)].map((_, index) => { return <div key={index}>アイウエオ</div>; })} </div> );
基本的には、そのオブジェクトなどが持つユニークなidをkey属性に指定します。
ユニークなidを持つオブジェクトの例
const listData = [{id:1,text:'1st'},{id:2,text:'2nd'},{id:3,text:'3rd'}];
参考
りあクト! TypeScriptで始めるつらくないReact開発 第3.1版【Ⅱ. React基礎編】 p.39~
Babelは「ES2015をコンパイルするコンパイラ」なのか、それとも「ES2015をトランスパイルするトランスパイラ」 なのか
2022年4月22日 りあクト! 第5章 JSXとコンポーネントの関係(p.35~)
JSXの基本構文
JSX構文
<MyComponent foo="bar">baz</MyComponent>
コンパイルされると 以下のReact.createElementの形式に変換されます。
React.createElement(component, props, ...children)
React.createElement(MyComponent, { foo: 'bar' }, 'baz');
React.createElementの実行結果が以下の形式になります。
{ type: 'MyComponent', props: { foo: 'bar', children: 'baz' }, key: null, ref: null, }
children
React.createElement()の第3引数に相当するものです。 JSX においては属性値ではなく子要素として記述され、呼び出された側のコンポーネントでは暗黙のpropsとして渡されます。
Props
/src/App.tsx
import React from 'react'; import Greets from './components/Greets'; . . . const App: React.FunctionComponent = () => ( <div className="App"> <Greets name="Patty" times={4}> "yano" </Greets> </div> ); export default App;
Greetsタグに注目してください。
- Propsとしてname="Patty" times={4} Props内のchildrenとして"yano"が渡されます。
- childrenは暗黙的に渡されます。
/src/components/Greets.tsx
import React from 'react'; type Props = { name: string; times?: number }; const Greets: React.FunctionComponent<Props> = (props) => { const { name, times = 1, children } = props; return ( <> {[...Array(times)].map((_) => ( <p>Hello, {name}! {children}</p> ))} </> ); }; export default Greets;
- 受け取ったPropsを分割代入しています。
- childrenは引数として渡すときは暗黙的に渡しますが、受け取るときは明示的にchilderenとして分割代入される形で受け取る事で値が扱えます。
- timesだけデフォルト値の1が設定されています。
- [...Array(4)]はconsole.log([...Array(4)]);で出力すると
[ undefined, undefined, undefined, undefined ]
となりmapメソッドで繰り返し処理を行う回数を指定しています。
通常のPropsの渡し方
times={4}
のように{}
による式埋め込みで値を渡します。- 文字列の場合は
name="patty"
のようにクォーテーションで囲む形式によって渡すこともできます。
HTMLエスケープされた文字列をPropsとして渡した場合
- コンポーネント側で復元されます。
子要素として文字列を記述するときの挙動
- 行の先頭と末尾の空白文字と空白行が削除されます。
- 改行は空白文字に置き換えられます。
propsとしてtrueを渡す場合
propsとしてBoolean値のtrueを渡す場合、値の記述を省略できます。
省略前
<MyButton color="blue" disable={true} />
省略後
<MyButton color="blue" disable />
参考
2022年4月21日 りあクト! 第5章 JSXの書き方(p.29~)
JSXの基本文法
tsconfig.jsonのjsxオプション
reactに設定している場合
- JSXの記述は
React.createElement(...)
のように変換されます。 createElement
メソッドの上位モジュールのReactをインポートする必要があります。
react-jsxにしている場合
- TypeScript4.1以降のReact17.0から導入された新しいJSX変換方式では
import React from 'react'
を省略できます。
jsxの構文
JSXは最終的にReactElement オブジェクトの生成式になります。 式であるがゆえに変数の代入、狭義のオブジェクトのプロパティ値にするなど、関数の引数や戻り値にするなどが出来ます。
{ }を使用して、JSXの式の中に別の式を埋め込むことも出来ます。
const name = 'Patty'; const greet = (name: string) => <p>Hello, {name || 'Guest'}!</p>; return <div>{greet(name)}</div>;
- { }の中でgreet(name)で関数コールをしています。
- greetの中でも||で条件分岐して要素を返します。
null, undefined, boolean値を{ }の中で使う
null, undefined, boolean値を{ }の中で使うと、何も出力されません。boolean値が{ }内で表示されないという性質を、三項演算子や論理演算子に応用することで、条件に応じたJSXや値を返すことができます。
const n = Math.floor(Math.random() * 10); // 0 〜 9 の整数を生成 const threshold = 5; return ( <div> {n > threshold && <p>`n` is larger than {threshold}</p>} <p>`n` is {n % 2 === 0 ? 'even' : 'odd'}</p> </div> );
- return文の最初の{ }内では、ランダムな整数が5より小さい場合はtrueとなり何も返しません。大きい場合は&&の左辺の要素が返されます。
- 最後のpタグの部分では、nが奇数が偶数かを三項演算子で判定して文字列を返しています。
JSXでの繰り返し処理
const list ['Patty', 'Rolley', 'Bobby']; if(!list) { return <p>no list</p>} return ( <ul> { list.map((name) => ( <li>Hello, {name}!</li> )) } </ul> );
- JSXが式を返す値なので
.filter(...).map(...)
のようにメソッドチェーンや演算子を用いることもできます。
JSXにおけるコメントの書き方
- JavaScriptと同様に
// コメント
/* コメント */
の記法で行います。
JSXに複数の要素が含まれるときはトップレベルが一つの要素でなければならない
以下コンパイルエラーになる例
const elems = ( <div>foo</div> <div>bar</div> <div>baz</div> );
解決するには一つのタグで囲います。 フラグメントを用いるのが良いです。
const elems = ( <> <div>foo</div> <div>bar</div> <div>baz</div> </> );
- 空のタグは
React.Fragment
というコンポーネントの省略記法です。
参考
2022年4月18日 りあクト! 第5章 なぜReactはテンプレートを使わないのか(p.18~)
なぜReactはテンプレートを使わないのか
JSファースト(思想)
- ReactではJavaScriptで一貫してviewのレンダリングも行います。
- JSXはオブジェクトを生成するためのJavaScriptの純粋な式であり、フレームワークから特別扱いされることはありません。
HTML テンプレート派 AngularJS、Angular、Vue.js、Ember.js、Aurelia、Svelte など
JS ファースト派 React、Preact、Mithril、Cycle.js、hyperapp など
JSファーストのメリット
- 決まりごとが少なく、JavaScriptの表現を活用してより短いコードでコンポーネントを記述することが出来ます。
- ReactのエラーはJavaScriptのエラーのため特定がしやすいです。
- JSXが純粋な式のため、静的解析や型推論に適しているため、IDEやLintといったツールのサポートが受けやすいことに加えて、TypeScriptとの相性が良いです。
テンプレートのデメリット
- フレームワーク独自の制御構文や各種バインディングなどの暗黙の文脈が多く、決まりごとが多いことで複雑化するほど開発効率が悪くなります。
- テンプレートとロジックで分けることで、コンポーネントが肥大化したときに再分割しづらくなってリファクタリングの障害になります。
- フレームワークによるコンパイルをはさむため、エラーがわかりづらく、読み取るにはしばしば熟練の技が必要になります。
なぜReactはViewをタグツリーで表現するのか
JSファースト派のフレームワークの内、React、Preact以外はHyperSctiptというライブラリをviewのレンダリングに使っています。
HyperScript
ReactにおけるReact.createElement()のメソッドコールに相当するものを汎用的に簡略化できるライブラリです。
クライアントまたはサーバー上で、JavaScriptを使用してハイパーテキストを作成します。 https://github.com/hyperhype/hyperscript (公式より)
var h = require('hyperscript') h('div#page', h('div#header', h('h1.classy', 'h', { style: {'background-color': '#22f'} })), h('div#menu', { style: {'background-color': '#2f2'} }, h('ul', h('li', 'one'), h('li', 'two'), h('li', 'three'))), h('h2', 'content title', { style: {'background-color': '#f22'} }), h('p', "so it's just like a templating engine,\n", "but easy to use inline with javascript\n"), h('p', "the intention is for this to be used to create\n", "reusable, interactive html widgets. "))
このようにJSON形式で記述することにより冗長なタグを書かなくて済む点がメリットです。
ただし、属性を持った要素がその子として複数のテキストまたは別の要素を持ってというようなノードツリーによるデータ構造を視覚的にわかりづらいです。
JSファーストで制御ロジックにマークアップが混在するからこそ、マークアップ部分が 視認できるタグを用いた構文が好まれているようです。そのため、Reactでは、JSXを用いてUIを表現しています。
React用の各種レンダラー
HyperScriptはハイパーテキスト、つまりHTMLのみを対象にしていますが、それに対してJSXはXML全般を表現するためのものです。 各プラットフォームに合わせて具現化するためのものがレンダラーで、レンダラーの共通の記述言語がJSXです。
レンダラー | 内訳 |
---|---|
React DOM | HTML DOM |
react-test-renderer | JavaScript |
React ART | HTML5 Canvas |
React Native | iOSおよびAndroidのネイティブアプリケーション |
React Native for Windows + macOS | WindowsおよびmacOSのネイティブアプリケーション |
React 360 | ブラウザ上で動くVRアプリケーション |
React-pdf | PDFドキュメント |
react-theree-fiber | WebGLによる3Dグラフィック |
React Figma | Figmaプラグイン |
React Sketch.app | Sketchファイル |
JSXは初見ではわかりにくく感じますが、JSXを使うことのメリットを学習してきました。次章からJSXの書き方を見ていきます。
JSXを使うメリット
- JavaScriptの構文が使えます。
- タグを使うので視認性に優れています。
- ESLintの恩恵を受けられます。
- JSXはJavaScriptの構文拡張なので、条件分岐や繰り返しにJavaScriptの構文を使うことが出来ます。
- TypeScriptは言語レベルでJSXをサポートしており、
tsconfig.json
の設定にJSXのオプションがあり、コンパイラのtscがJSXをReact.createElement()
などの関数コールに変換してくれます。
関数コール(関数呼び出し)
関数呼び出しとは、プログラムから関数サブプログラムや関数ライブラリを呼び出すことである。 プログラムはいわば関数の組み合わせであるから、プログラムの処理は関数呼び出しを行いながらなされる。