- 投稿日:2021-06-23T23:49:51+09:00
TypeScript + Reactを勉強するために数独を解くアプリを作った話(処理編)
画面作る編の続きです。 普段数独で遊ぶときの思考をプログラムにすることで、TypeScriptとReactを使ってみる試みです。 当方、攻略サイトを見たことがないのですごく効率が悪いかもしれません。 下ごしらえ 行、列、グループ内のセルをまとめて操作する役割のクラスを作る 行、列、グループの何れにせよ、内部のマスをまとめて操作できると楽なので、 その様な役割を担うCellSetクラスを作成します cellSet.tsx import CellData from "./cellData" export default class CellSet { id : number; cells: CellData[]; constructor(id:number, cells: CellData[]){ this.id = id; this.cells = cells; } /** 渡されたマス以外のマスから、可能性を消す */ erasePossible = (cellData: CellData) :void => { this.cells.filter(x => x.id !== cellData.id).forEach(c => c.erasePossible(cellData.value)); } } CellSet等をまとめてstateに保持したいので、いっそStateクラスを作って一つにまとめます CellSetが内包するCellDataとCellSetは相互に参照できた方が楽なので、 CellDataにCellSetインスタンスを渡すようにします。 cellData.tsx import CellSet from "./cellSet"; export default class CellData { ... // メンバー追加 group: CellSet | null = null; row: CellSet | null = null; column: CellSet | null = null; } state.tsx import CellData from "./cellData"; import CellSet from "./cellSet"; import Const from "./const"; export default class State { /** 81マス */ cellDatas: CellData[]; /** 9グループ分のCellSet */ groups: CellSet[] = []; /** 9行分のCellSet */ rows: CellSet[] = []; /** 9列分のCellSet */ columns: CellSet[] = []; constructor(cells:CellData[]){ this.cellDatas = cells; this.const = Const.getInstance(); this.const.nineValues.forEach( id => { /* グループ用CellSetの生成、マスからもCellSetにアクセスできるようにする */ const targetGroupCells = cells.filter(x => x.groupId === id); const group = new CellSet(id, targetGroupCells); targetGroupCells.forEach( c => c.group = group ); this.groups.push(group); /* 行用の以下略 */ const targetRowCells = cells.filter(x => x.rowId === id); const row = new CellSet(id, targetRowCells); targetRowCells.forEach(c => c.row = row); this.rows.push(row); /* 列用の以下略 */ const targetColumnCells = cells.filter(x => x.columnId === id); const column = new CellSet(id, targetColumnCells); targetColumnCells.forEach(c => c.column = column); this.columns.push(column); }); } } stateに保持するクラスが変わるので反映します App.tsx export default class App extends React.Component < {}, {model: State}> { //変更 constructor(props: any){ super(props); this.state = { model: new State(this.createDummyCellDatas()) } //変更 this.handleChange = this.handleChange.bind(this); this.readXlsxFile = this.readXlsxFile.bind(this); } /** 1マスの表示切替 */ private changeViewMode = () => { let model: State = this.state.model; model.cellDatas.forEach( c => c.viewMode = c.viewMode === ViewMode.Value ? ViewMode.Possibles : ViewMode.Value); //変更 this.setState({model: model}); } /** XLSXファイルを読み込み、CellDataを生成してstateに格納する */ private readXlsxFile = (file: File) => { const reader = new FileReader(); reader.onload = (e) => { ... this.setState({ model: new State(cells) }); //変更 } reader.readAsArrayBuffer(file); } } 1マスの値が確定したとき、行・列・グループから可能性を消す 1マスの値が確定したときの処理を作ります。 ①valueに確定値を入れ、可能性をクリアする ②グループ・列・行の確定マス以外から可能性を消す処理をします。 CellDataクラスに値を確定させるdetermineメソッドを作成し、 行・列・グループのerasePossibleメソッドでまとめて可能性を消し込みます cellData.tsx export default class CellData { /** 値を確定する */ determine = (value: number) : void => { this.value = value; this.possibles = [] this.group?.erasePossible(this); this.row?.erasePossible(this); this.column?.erasePossible(this); } } 解く ファイル読み込み直後の可能性消し ファイルを読み込んだ時点で値が決まっているマスをもとに可能性を消します App.tsx export default class App extends React.Component < {}, {model: State}> { /** XLSXファイルを読み込み、CellDataを生成してstateに格納する */ private readXlsxFile = (file: File) => { const reader = new FileReader(); reader.onload = (e) => { ... //追加 const state = new State(cells); state.init(); this.setState({ model: state }); } reader.readAsArrayBuffer(file); } } state.tsx export default class State { ... init = () => this.cellDatas.filter( c => c.value).forEach( c => c.determine(c.value) ); } これにより、以下のデータを読み込んだ直後は、 可能性表示がこの様になります グループ・列・行の中で、1マスしか可能性が残ってない場合は確定する 以下のように、可能性を取りうるマスが一つしかない場合は確定します cellSet.tsx import CellData from "./cellData" import { Const } from "./const"; //追加 export default class CellSet { ... /** * 集合内のセルで、可能性を持つマスが一つだけの場合は確定する */ determineJustOnePossible = () => { Const.NINE_VALUES.forEach( n => { let hasPossibleCells = this.cells.filter( c => c.possibles.includes(n)) ; if(hasPossibleCells.length === 1){ hasPossibleCells[0].determine(n); } }); } } state.tsx export default class State { ... culcNextState = () => { [ this.groups, this.rows, this.columns ].flat().forEach( g => g.determineJustOnePossible() ); } } App.tsx export default class App extends React.Component<{}, { model: State }> { /** * 確定値の探索 */ private executeNext = (): void => { let state:State = this.state.model; state.culcNextState(); this.setState({model: state}); } } 1グループ内で可能性が並んでいる場合は、他グループの同じ行位置・列位置から可能性を消す 1つのグループ内で、 ①可能性が2マスか3マスまで絞られている ②一列に並んでいる とき、同じ行位置、または列位置の他マスがもつ可能性を消します state.tsx export default class State { ... culcNextState = () => { [ this.groups, this.rows, this.columns ].flat().forEach( g => g.determineJustOnePossible() ); this.erasePossibleFromAlinedPossible(); // 追加 } /** * 残っている可能性が並んでいるとき、ほかグループのマスから可能性を消す */ erasePossibleFromAlinedPossible = () => { Const.NINE_VALUES.forEach( n => { this.groups.forEach( group => { // 2マスか3マスまで絞られているか const cells = group.cells.filter( c => c.possibles.includes(n) ); if(cells.length !== 2 && cells.length !== 3){return;} const rowId = cells[0].rowId; const columnId = cells[0].columnId; const otherGroupCells = this.cellDatas.filter( c => c.groupId !== group.id ); // 絞られたマスは行方向に並んでいるか? 並んでいたら別グループの同行マスから可能性を消す 行方向でも同じことをする const targetCells = cells.every( x => x.rowId === rowId ) ? otherGroupCells.filter( g => g.rowId === rowId) : cells.every(x => x.columnId === columnId) ? otherGroupCells.filter( c => c.columnId === columnId) : []; targetCells.forEach(c => c.erasePossible(n)); }); }); } } ペアがあったら、ペア値以外の可能性を消す 一つの行・列・グループの中において、「残った可能性が2つで、可能性の数字が同じマス」がある場合、 その2つを勝手にペアと呼ぶことにします 場合によっては、マスの値が確定していなくても確定したものとして考えられます ペアが見つかると、その2つの可能性以外は一旦考えなくて良くなります 以下の様な場合、4・6でペアができ、3,1,7の可能性を消せます また、その結果1・3でペアができるので、7の位置が確定します。 cellSet.tsx /** * ペアを探し、発見したらそれ以外の可能性を消す */ findPairs = () => { Const.NINE_VALUES.forEach(n1 => { // 2マスまで絞れている可能性を抽出(p1) const p1 = this.cells.filter(x => x.possibles.includes(n1)); if (p1.length !== 2) { return; } Const.NINE_VALUES.filter(n2 => n2 > n1).forEach(n2 => { // p1以外で、2マスまで絞れている可能性を抽出(p2) const p2 = this.cells.filter(x => x.possibles.includes(n2)); if (p2.length !== 2) { return }; // p1とp2が同じマスである場合はペアを作成。ペアの値以外を可能性から消す。 if (p1.every(p => p2.includes(p))) { p1.forEach(p => { p.possibles = [n1, n2]; }); } }); }); } state.tsx export default class State { culcNextState = () => { ... [ this.groups, this.rows, this.columns ].flat().forEach( g => g.findPairs() ); //追加 } } 実行 ここまで実装してテストしたところ、手持ちの問題が結構解ける様でした 画面作成編で使っていた問題を入れてみます 読み込み直後 Execute 1回目 Execute 2回目 Execute 3回目 Execute 4回目 Execute 5回目 Execute 6回目 Execute 7回目 解けました。 この実装は可能性から推測できる問題にのみ使えるので、 推測だけでは解けないトライアンドエラーが必要となる問題に対しては無力です そういう問題に対してはバックトラック法を使うことになります 感想 ・思っていたよりも経過がわかりにくかったので、差分は赤色で表示したほうが良かった ・stateで保持するクラスにメソッドを持たせると、改修が重なって行くにつれて巨大化しそう。どうなんだろう? ・型があるとわかりやすいと改めて思いました。でもJavaScriptが好きな人は逆に嫌いそう
- 投稿日:2021-06-23T23:09:11+09:00
ReactのInput × UseStateで便利な機能を実現
Input×UseState const [text, setText] = useState(); const onChangeText = (e) => { setText(e.target.value); } const onClickButton = () => { setText(""); } return ( <input placeholder="Textを入力" value={text} onChange={onChangeText}/><br /> <button onClick={onClickButton}>クリア</button> ); 上記のように、コンポーネントの中で、ステート変数を一つ宣言します。 そして、returnの中でinputタグを使って入力フォームを作ります。 inputの入力欄に文字を入力すると、onChangeに渡したonChangeText関数が実行され、入力した文字がステート変数にセットされるという処理になっています。 また、buttonタグでクリアボタンを作成したとします。 ボタンを押すと、onClickに渡したonClickButton関数が実行され、textの設定値が""になり、初期化されるようになっています。 inputのvalue={text}という箇所があることで、このようなクリアボタンを設置すると、ボタンを押すことでinputの入力欄を空白にリセットすることもできます。 onClickButton関数の中の処理に、textの値を使って何らかの処理を行うことで、inputに入力した値に応じた処理が適宜実行でき、また、同時にinputの入力欄もリセットできます。 まとめ Reactの勉強をしていて、これ便利だなと思ったので、忘れないように記事にさせていただきました。 初学者の方の参考になれば、とても嬉しいです。
- 投稿日:2021-06-23T22:22:27+09:00
React + Rails API 環境構築 ①React側の設定 〜ESLintの設定まで
概要 1からSPAアプリケーションの環境を作っていくので、その手順を残す 構築していく途中で調べた内容も別途記事にしてまとめているので良ければ見てください フロントエンド環境構築 1. node.jsをインストール〜Linter&Formatterの設定前まで anyenv1をインストール(env系をマネージするパッケージマネージャー) brew install anyenv echo ‘eval “$(anyenv init -)”’ >> ~/.zshrc Exec $SHELL -l anyenv経由でnodenvをインストールする anyenv install nodenv exec $SHELL -l anyenvとnodenv用のプラグインを入れる nodenvを含めたenv系をまとめてアップデートしてくれるanyenvプラグインのanyenv-update npmインストール時にデフォルトで一緒にインストールしておくパッケージを指定できるnodenvプラグインのnodenv-default-packages2 mkdir -p $(anyenv root)/plugins git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update mkdir -p "$(nodenv root)"/plugins git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages" nodenv rootにdefault-packagesを作成し、中身を以下のように編集する touch $(nodenv root)/default-packages yarn Typescript ts-node typesync node.jsをインストールする anyenv update # インストール可能なリストを更新する(これしないとNode最新版にあげられない) nodenv install -l #インストールできるバージョンを一覧で確認 nodenv install 14.4.0 #バージョンを指定してインストール nodenv global 14.4.0 #端末上でデフォルトで使用するnode.jsのバージョンを指定 nodenv local 14.4.0 #カレントディレクトリ配下で使用するnode.jsのバージョン指定 create-react-appでreactアプリの雛形を作成する npx create-react-app <directry name> —template typescript #reactアプリを始めるためのディレクトリが既にある場合は「.」、まだない場合はディレクトリ名を設定する #—template typescriptでTypeScriptのためのテンプレートを指定する typesync3をインストールする reactのプラグインを入れた後にtypescriptの型情報を一緒にインストールできるようにするプラグイン yarn add type sync tsconig.jsonファイルを編集する tsconfig.json “Jsx”: “react-jsx”, + “baseUrl”: “src”, #絶対パスでimportできるようにする設定 + “downlevelItteration”: true # vscodeで開発するときは上記の設定をした後にコンソールから touchtsconig.json をして設定を読み込ませる 2. Linter&Formatterの設定 TypeScript他パッケージを最新にする yarn upgrade-interactive —latest #対話的にyarnで管理しているライブラリを更新する yarn upgarade typescript@latest #typescriptを最新にする ESLint4をインタラクティブにインストールする $ yarn eslint --init ? How would you like to use ESLint? 》To check syntax, find problems, and enforce code style ? What type of modules does your project use? JavaScript modules (import/export) 》JavaScript modules (import/export) ? Which framework does your project use? 》React ? Does your project use TypeScript? 》Yes ? Where does your code run? 》Browser ? How would you like to define a style for your project? 》Use a popular style guide ? Which style guide do you want to follow? 》Airbnb: https://github.com/airbnb/javascript ? What format do you want your config file to be in? 》JavaScript The config that you've selected requires the following dependencies: eslint-plugin-react@^VERSION @typescript-eslint/eslint-plugin@latest eslint-config-airbnb@latest eslint@^VERSION eslint-plugin-import@^VERSION eslint-plugin-jsx-a11y@^VERSION eslint-plugin-react-hooks@^VERSION @typescript-eslint/parser@latest ? Would you like to install them now with npm? 》No yarnで先ほどの拡張ルールやプラグインをインストールする yarn add -D eslint-plugin-react @typescript-eslint/eslint-plugin \ eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y \ eslint-plugin-react-hooks @typescript-eslint/parser yarn #package.jsonのscriptsにpreinstallでtypesyncする設定にしていない場合はこれの前にtypesyncする .eslintrc.jsファイルをカスタマイズする extendsに記述するのは各プラグインルールの推奨の共有設定 プラグインは読み込んだだけでは何のルールも適用されない プラグイン開発者がパッケージ内で共有設定を一緒に提供しているので、それをextendsで適用する必要がある 共有設定間でルールが衝突した場合は後の記述が上書きするため、順番には注意する .eslintrc.js extends: [ 'plugin:react/recommended', 'airbnb', + 'airbnb/hooks', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', ], parserの設定 @typescript-eslint/parserに渡すオプションを定義する .eslintrc.js parser: '@typescript-eslint/parser', parserOptions: { ecmaFeatures: { jsx: true }, ecmaVersion: 12, + project: './tsconfig.eslint.json', sourceType: 'module', + tsconfigRootDir: __dirname, }, parserに読ませるファイルを作成する(tsconfig.jsonはそのまま読ませない) パーサがnpmパッケージのファイルまでパースし、VSCodeと連携させたときのパフォーマンス低下、新規ファイルのパースの失敗を防ぐため 現状ではこうするしかないらしい(今後対応が変わる可能性は大いにある) tsconfig.eslint.json { "extends": "./tsconfig.json", "include": [ "src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx" ], "exclude": [ "node_modules" ] } pluginsの設定 yarn addしたプラグインのルールを適用するための内容を追加する 追加ルールのプラグインはyarn addしただけでは適用されないのでここにルールを追加する必要がある ESLintはデフォルトでは親ディレクトリの設定ファイルまで読み込むようになっているので、それを抑止するためにrootのオプションを追加している .eslintrc.js plugins: [ '@typescript-eslint', + 'import', + 'jsx-a11y', 'react', + 'react-hooks', ], + root: true, rulesの設定 rulesに各種ルールを設定する 各ルールの適用の可否やエラーレベルを設定する 主にextendsで読み込んだ共有設定の値を書き換えるときにここに記述する 各種ルールの説明 1. no-use-before-define, @typescript-eslint/no-use-before-define 定義前の変数の使用を禁じる ESLint と TypeScript ESLint のルール。本来なら上記でやっているような記述は必要なく TypeScript で正常に動作するはずだが、不具合が解消されてはリグレッションが繰り返されており、react-scripts 4.0.1 の環境ではこうしないと import React from 'react' のインポート文でエラーになってしまう 2. lines-between-class-members クラスメンバーの定義の間に空行を入れるかどうかを定義するルール。eslint-config-airbnb で常に空行を入れるように設定されていたのを、ここでは 1 行記述のメンバーのときは空行を入れなくていいようにしている 3. no-void void 演算子の(式としての)使用を禁ずるルール。Effect Hook20 内で非同期処理を記述する際、@typescript-eslint/no-floating-promises ルールに抵触してしまうのを回避するのに void 文を記述する必要があるため、文としての使用のみを許可している 4. padding-line-between-statements 任意の構文の間に区切りの空行を入れるかどうかを定義するルール。ここでは return 文の前に常に空行を入れるよう設定している 5. @typescript-eslint/no-unused-vars 使用していない変数の定義を許さないルール。ここでは変数名を _ にしたときのみ許容するように設定 6. import/extensions * インポートの際のファイル拡張子を記述するかを定義するルール。npm パッケージ以外のファイルについて .js、.jsx、.ts、.tsx のファイルのみ拡張子を省略し、他のファイルは拡張子を記述させるように設定 7. react/jsx-filename-extension * JSX のファイル拡張子を制限するルール。eslint-config-airbnb で .jsx のみに限定されているので、.tsx を追加 8. react/jsx-props-no-spreading JSX でコンポーネントを呼ぶときのprops の記述にスプレッド構文を許さないルール。eslint-config-airbnb にてすべて禁止されているが、 のように個々の props を明記する書き方のみ許容するように設定 9. react/react-in-jsx-scope JSX 記述を使用する場合に react モジュールを React としてインポートすることを強制する。新しい JSX 変換形式を用いる場合はインポートが不要になるためこの設定を無効化 10. react/prop-types * コンポーネントの props に型チェックを行うための propTypes プロパティ21の定義を強制するルール。eslint-config-airbnb で設定されているが、TypeScript の場合は不要なのでファイル拡張子が .tsx の場合に無効化するよう設定を上書き .eslintrc.js rules: { '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'], }, }, }, }; .eslintignoreファイルを作成する ESLintチェックの対象外にするための設定ファイル 関係ないコードまでlintが走って不要な場面でエラーが出るのを防ぐため build/ Public/ **/coverage/ **/node_modules/ **/*.min.js *.config.js .*lintrc.js setings.jsonに追加で設定をする ファイル保存時にVSCode内蔵のものではなく、ESLintの自動整形が走るようにする 最後の行はプロジェクトにTypeScriptがインストールsれている場合はどっちを使うか尋ねさせるための設定 settings.json "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "editor.formatOnSave": false, "eslint.packageManager": "yarn", "typescript.enablePromptUseWorkspaceTsdk": true, ...React + Rails API 環境構築 ②React側の設定 〜Prittier,stylelintの設定 に続く リンク集 anyenvまとめ ↩ nodenv-default-packagesまとめ ↩ typesyncまとめ ↩ ESLintまとめ ↩
- 投稿日:2021-06-23T13:58:32+09:00
【JavaScript】js-cookieでcookieを操作する
インストール npmを使う場合は、 npm npm i js-cookie yarnを使う場合は、 yarn yarn add js-cookie 使用方法 使うファイルにimportします。 import import Cookies from 'js-cookie'; cookieに値を保存 cookiesに値を保存する Cookies.set('cookie_name', 'value') //7日後に有効期限が切れる、vokkiesを保存する Cookies.set('name', 'value', { expires: 7 }) //pathを指定して、それ以下のpathでのみ有効なcookieを保存する Cookies.set('name', 'value', { expires: 7, path: '' }) cookieに値を取得する cookieに値を取得する Cookies.get('name') // => 'value' //cookieで取得できるすべての値を取得する Cookies.get() // => { name: 'value' } ちなみに、指定したcookieが存在しない場合、undefindedを返します。 cookieで指定した値がない Cookies.get('hoge') //=> undefined cookieに値を削除する cookieに値を削除する Cookies.remove('name') 参考
- 投稿日:2021-06-23T10:39:53+09:00
ReactでTextareaの高さを可変にする
問題点 textareaのstyleにline-heightを指定すると 入力された値が1行の時でも、余分に余白ができてしまった。。。 textareaの高さを入力された行数によって、可変にしたい。 結論 textareaのrow属性を動的に指定して、実現できた。 コード(React_classコンポーネント) constructor() constructor(){ super(); this.state={ text:"サンプルテキスト" } } 高さを計算する関数 textarea内の改行は\nで取得できる 入力値を\nで分割すれば、改行数が取得できる calcTextAreaHeight(value){ let rowsNum = value.split('\n').length; return rowsNum } render() <textarea defaultValue={this.state.text} rows={this.calcTextAreaHeight(this.state.text)} onChange={e => this.setState({text:e.target.value})} />
- 投稿日:2021-06-23T10:29:24+09:00
【個人的よく使う】React Redux Firebase Material-UIの環境構築
はじめに Reactで個人開発する際、個人的によく用いるReact, Firebase, Material-UIの環境構築 React まずReactの環境構築 便利 % npx create-react-app[プロジェクト名] Firebase Firebaseのコンソールから新しいプロジェクトを作成 作成完了したらアプリ作成でWebアプリ 設定画面でデフォルトGCPリソースロケーションをasia-noetheast1に設定 Cloud Firestoreでデータベースを作成する ReactとFirebaseをコネクトする create-react-appしたディレクトリでターミナルを操作する % Firebase login コマンドを実行しFirebaseのプロジェクトを作成したGooGleアカウントでログイン % firebase init 色々質問される どのサービスを使うか選択する時 Firestore Functions Hosting Storage はよく使う 【重要】 What do you want to use as your public directory ? と聞かれる デフォルトはpublicだが、Reactは本番に公開するフォルダはbuildなので「build」を指定 パッケージをインストール よく使うのは Material-UI => UIライブラリ connected-react-router => dispatch(push('/'))などpushメソッドでよく使う History => 今どこのURLにいて以前はどこにいたのか記憶する react-redux => Reactと一緒にReduxを使うパッケージ react-router => ルーティングを行うパッケージ redux-actions => Reduxでアクションを作るときに使うパッケージ redux-logger => Reduxのstateが変化したときに何から何に変化したのかログを出す便利機能 redux-thunk => Reduxの中で非同期処理を行える Firebase Redux % npm install --save @material-ui/core @material-ui/icons @material-ui/styles connected-react-router firebase history react-redux react-router redux redux-actions redux-logger redux-thunk reselect Firebaseにデプロイする デプロイするためにはまずローカルで作成したReactのアプリを公開できる形にする必要がある つまりbuildする % npm run build Firebaseでデプロイ % firebase deploy 完了するとURLが返ってくる
- 投稿日:2021-06-23T07:20:20+09:00
今更ながらRedux Toolkitに入門してみた①
はじめに 今更ながらですがRedux Toolkit入門編①です。 ②ではRedux Toolkitの主な機能を紹介し ③ではそれらを踏まえてTodoリストの作成をします。 間違っているところがあれば指定してくれると助かります! この記事ではTypeScriptを使って行うのでTypeScriptを軽くわかった方向けになります。 また下記で行っているテンプレートの作成は最新版で少し中身が変わっている点があるのでご了承ください。 そもそもReduxって何?ってなる人向け Reduxは状態を管理してくれるフレームワークです。 開発の規模が大きくなるほど開発が容易になったりすることができるので大規模な開発においてよく使われていたりします。 Reduxの流れ ユーザの操作により、componentのstateが変更される Action Creatorによって、Actionを生成する そのActionをdispatchする(Storeに送信) ActionとstateをReducerが受け取り、storeのstateを更新する 最後にstoreから更新されたstateが渡ってくる 大まかなまとめ state stateとは日本語で状態を意味しています。その名前の通り そのComponentが持っている状態のことを示します。 Action アクションはUserがinputなど、何かのアクションが起きた時、Storeにどんなデータを利用するかということを定義する。その際にActionCreatorsによってActionを生成する。 Store Storeとはアプリケーションの全てのStateを管理してくれる場所 Dispatch ActionCreatorで生成されたActionを呼び出してStoreに渡してくれます。 storeにdispatchというメソッドを用意して、引数でアクションを呼び出しで使います。 reducer storeで受け取ったアクションを読み取り、stateを更新する必要があればstateを更新する処理を行う場所 ざっくりとした説明ですが最下部に参考文献があるのでそちらをみてさらに理解を深めるといった形がおすすめです。 上記を踏まえた上でRedux Toolkitとはなんぞや 簡潔にいうとReduxを簡潔に使用できるツールキットです。 Redux Toolkitは公式に作られたツールでReduxの公式サイトでも紹介するページがあります。 そこに書かれている記事を抜粋し,なぜRedux Toolkitが作成されたかというと 「Redux storeの設定は複雑すぎる」 「Reduxに何か便利なことをさせるには、たくさんのパッケージを追加する必要がある」 「Reduxにはボイラープレートコードが多すぎる」 実装方法 create-react-appの場合 mkdir practice cd practice npx create-react-app . --template redux-typescript 上記のテンプレートはRedux Toolkitが使われていて簡単に実装ができるのでめんどくさい設定が嫌いな方はこれでいいと思います。 既存のアプリケーションに導入する場合 npm install @reduxjs/toolkit 参考文献 参考にさせていただいたきました!!ありがとうございます!! https://note.yuuniworks.com/study/redux-toolkit.html#%E5%90%AB%E3%81%BE%E3%82%8C%E3%82%8B%E3%82%82%E3%81%AE
- 投稿日:2021-06-23T04:20:20+09:00
node.js、express、handlebarsを使ってウェブサーバーの立ち上げからページの制作までをやってみた - 2
次はNode環境で、ウェブサイトを作っていこう 前回からの続き、expressをインストールし、サーバーも立ち上がったので、早速ウェブサイトを作っていくよ。 通常のサイト制作と大きく違っているのは、まず、ディレクトリの構造。 前回もやったけど、エンドポイントって考え方がメインになるので、旧来の絶対パスとか相対パスっていう考え方とはちがうなぁという感じ。 基本、静的データと動的データを分けて、expressに登録していくんじゃ。 あと、テンプレートエンジンと言われているプラグインをexpressに入れて、ページを作っていくよ。 何故そんなものを使うかと言うと、ただの静的なサイトをテキストエディタでもIDEでも使って作って登録した静的フォルダにいれれば、今までのような阿部 寛的なページは勿論作れるのじゃが、php的な作り方。つまり、ヘッダーとかフッタとかの使い回す部分を分けておいて、ページ読み込み時に合体させるみたいな事をするには、このテンプレートエンジンを使うのじゃ。 名前の通り、”テンプレート”を生成するエンジンじゃの。 色々種類はあるが、自分はhandlebarsが使いやすいので、これで解説するぞ。いいな! handlebarsではなくて、それのexpress用エンジンhbsをインストール。 ~/myProject/web-server$ npm i hbs up to date, audited 89 packages in 2s 2 packages are looking for funding run `npm fund` for details found 0 vulnerabilities そして、expressにテンプレートエンジンとして登録。 app.set('view engine', 'hbs'); 次に、publicディレクトリを制作。 ここに静的なファイルは保存していく。img,css,クライアント側jsなんかじゃな。あと、岩のようなhtmlページとか。 それが出来たら、publicディレクトリをexpressに登録。 その前に、pathモジュールをダンロードして、インストール。 npm i path const path = require('path'); nodeのサイトは、app.jsが全ての起点になるのと、サーバーにデプロイする際に、ディレクトリの構造が従来のサイト構造と違っているので、app.jsファイルを基点にモジュールでパスをしっかりと確定しておかないとややこしい事になる。なので、モジュールで指定する。 const publicDir = path.join(__dirname, "../public") app.use(express.static(publicDir)); 何をやっているかと言うと、ルートフォルダからのpublicディレクトリへのパスをモジュールで割り出して、expressで静的ディレクトリとして登録している。静的なファイルは全部ここやでとexpressに伝えている。 そして、viewsディレクトリを作る。テンプレートエンジンで制作したページは、基本ここに保存していくのである。 静的ファイルはpublic、動的ファイルはviews、nodeのローレベルファイルはルート直下っていうイメージでディレクトリを整理していくと良いよ。多分! で、その中にindex.hbsを作ろう。ここがランディングページになる。 hbsって言っても、普通のhtmlページ。 index.hbs <!DOCTYPE html> <html lang="en"> <head> <title>Node de Site</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Title here</h1> </body> </html> で、前回やったルーティング。 このindex.hbsをルートページにする。sendでなくて、render。テンプレートをレンダリングする。 app.get('',(req, res)=>{ res.render('index') }) で、一度サーバーを立ち上げて、ページを表示させてみよう。 ~/myProject$ node src/app.js server running on Port:3030 ブラウザで localhost:3030/ にアクセスすると、ページが表示されてるね。 次は、少し動的に、ページのタイトルとサイトの説明文を挿入してみよう。 {{}}で囲ったところに、変数を入れると、レンダリング時に表示される。 index.hbs <!DOCTYPE html> <html lang="en"> <head> <title>Title here</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>{{title}}</h1> <h3>{{description}}</h3> </body> </html> app.js app.get('/',(req, res)=>{ res.render('index', { title: "Hishori Abe no site", description: "Welcome to fastest site in japan" }) }) で、ブラウザでローカルにアクセスし、表示するとこうなってるはず。 ここまでで、node環境にexpressをインストールし、handlebarsを使って、htmlページをサーバー上で表示することが出来たんじゃ。 app.jsファイルはこんな感じ。 const path = require('path'); const express = require('express'); const app = express(); const port = process.env.PORT || 3030; app.set('view engine', 'hbs'); app.get('/',(req, res)=>{ res.render('index', { title: "Hishori Abe no site", description: "Welcome to fastest site in japan" }) }) const publicDir = path.join(__dirname, "../public") app.use(express.static(publicDir)); // routing // / => root app.get('/', (req, res)=>{ res.send('Hello Express!'); }) app.listen(port, ()=>{ console.log(`server running on Port:${port}`); }) バイトの時間がきたんで、handlebarsを使ってページをテンプレート化していくのは次回じゃ! じゃあの。