React 16のライフサイクルの私的整理
React Component内で何か処理を行おうとした際、毎回koba04さんのこの記事を参照して、Reactのライフサイクル的にどこが適切な実装箇所かを確認して実装している。でも毎回調べるのは時間がもったいないので、個人のメモとしてまとめる。
lifecycle diagram
ライフサイクルの流れが図でわかる。
やりたいこと別スニペット
公式の例より。後で追記して全パターンまとめる。
stateを初期化したい
stateを定義して初期化。
class ExampleComponent extends React.Component<ExampleProps, ExampleState> { state = { currentColor: this.props.defaultColor, palette: 'rgb', }; }
データをAPIでfetchしたい
componentDidMount
内に実装。
class ExampleComponent extends React.Component { state = { externalData: null, }; componentDidMount() { this._asyncRequest = loadMyAsyncData().then( externalData => { this._asyncRequest = null; this.setState({externalData}); } ); } componentWillUnmount() { if (this._asyncRequest) { this._asyncRequest.cancel(); } } render() { if (this.state.externalData === null) { // Render loading state ... } else { // Render real UI ... } } }
propsの値に応じてstateを更新したい
getDerivedStateFromProps
を使う。getDerivedStateFromProps
を実装すると、戻り値でstateが更新されるらしい(nullを返却すれば更新されない)。
class ExampleComponent extends React.Component { // Initialize state in constructor, // Or with a property initializer. state = { isScrollingDown: false, lastRow: null, }; static getDerivedStateFromProps(props, state) { if (props.currentRow !== state.lastRow) { return { isScrollingDown: props.currentRow > state.lastRow, lastRow: props.currentRow, }; } // Return null to indicate no change to state. return null; } }
propsの変化に応じて副作用のある処理を実行したい
componentDidUpdate
内に実装する。propsが変更される場合に必ず実行されるので、prevProps, prevStateと比較して無駄に初理が実行されないように気をつける。
class ExampleComponent extends React.Component { componentDidUpdate(prevProps, prevState) { if (this.props.isVisible !== prevProps.isVisible) { logVisibleChange(this.props.isVisible); } } }
propsの変化に応じて外部からデータを取得したい
getDerivedStateFromProps
でstateをpropsの変化に応じて更新し、componentDidUpdate
でstateの変更を検知してデータの取得を行う。
class ExampleComponent extends React.Component { state = { externalData: null, }; static getDerivedStateFromProps(props, state) { // Store prevId in state so we can compare when props change. // Clear out previously-loaded data (so we don't render stale stuff). if (props.id !== state.prevId) { return { externalData: null, prevId: props.id, }; } // No state update necessary return null; } componentDidMount() { this._loadAsyncData(this.props.id); } componentDidUpdate(prevProps, prevState) { if (this.state.externalData === null) { this._loadAsyncData(this.props.id); } } componentWillUnmount() { if (this._asyncRequest) { this._asyncRequest.cancel(); } } render() { if (this.state.externalData === null) { // Render loading state ... } else { // Render real UI ... } } _loadAsyncData(id) { this._asyncRequest = loadMyAsyncData(id).then( externalData => { this._asyncRequest = null; this.setState({externalData}); } ); } }
参考
Apollo ClientのrefetchQueriesの型
何度も調べそうなので、Apollo ClientのrefetchQueriesのFlow Typeをメモ。
// @flow import type { ExecutionResult } from 'graphql'; import type { RefetchQueryDescription } from 'apollo-client'; type HogePayloadType = { // ... refetchQueries?: RefetchQueryDescription | ((r: ExecutionResult) => RefetchQueryDescription), // ... }
参考
自分の頭の中のdocker-compose.yamlを2から3にupgradeする
docker-compose.yaml
を作成する時は他のプロジェクトのyamlファイルを元に作成するため、いつまでもファイルの1行目がversion: '2'
で始まるので、docker-composeファイルの2と3で何が違うかを調べたメモ。
個人メモ
volume_driver
,volumes_from
についてはversion3ではvolumesを使えばよいcpu_shares
,cpu_quota
,cpuset
,mem_limit
,memswap_limit
はresource → deploy配下に移ったextends
は現時点ではversion3でサポートされてないけど、https://github.com/moby/moby/issues/31101次第でサポート再開されるかもgroup_add
はなくなったlink_local_ips
とpids_limit
はまだ使えないらしい
私的結論
ファイルの1行目がversion: '3'
に書き換えても問題なさそう。
参考
CSSのcursorプロパティを知らなかったので調べる
CSSは人のコードから拝借して実装することが多いので、CSSのプロパティを詳しく理解せず使っていることがよくある。その辺を解消するために、最近使ったcursorプロパティを調べたメモ。
cursorプロパティの値
/* キーワード値でカーソルの形を指定できる */ cursor: wait; / * カーソルの形は画像でも指定できる。URLが解決できなかった場合のフォールバックを指定する */ cursor: url("hoge.svg") url("fuga.png"), progress; /* x, yでカーソルのクリック座標の位置を指定できる。カーソルの左上が基準座標。 */ cursor: url("hoge.svg") 3 3, auto;
参考
Babel7にバージョンアップ時に辿り着いたissueとworkaroundな対応メモ
Babel7にバージョンアップした際に、あれこれ調べてworkaroundな対応を行ったメモ。
storybook
regeneratorRuntime is not defined
というエラーになるので、addons.js
とconfig.js
で@babel/polyfill
をimportするようにした。
※issueの内容自体はbabel7と関係ないけど、これで上手くいったので引用。
// .storybook/addons.js, .storybook/config.js import '@babel/polyfill';
lint
TypeError: Cannot read property 'range' of null
になるので、eslintの設定を.eslintrc
に切出しして、一時的にindent
とtemplate-curly-spacing
をoffにした。
"rules": { "template-curly-spacing": "off", "indent": "off", }
build + jest
jest側はbabel.config.js
に設定を切り出ししないとbuildに失敗するが、babel-node側ではbabel.config.js
を参照してくれない場合があるため(dockerでbabel-node build
的な処理を行った場合に発生した)、babel.config.js
を追加してpackage.json
の設定を参照するようにした。
// babel.config.js const pkg = require('./package.json'); module.exports = { presets: pkg.babel.presets, env: pkg.babel.env, plugins: pkg.babel.plugins, };
flexboxについてのメモ
Babel7のpolyfillのこれまでと違う点のメモ
babel7から@babel/polyfill
にproposal polyfill(stage-3以下のpolyfill)が含まれなくなり、core-jsからのimportを個別に行う必要があると知ったのでメモしておく。
import "core-js/fn/array/flatMap";
の様に使っているものだけ個別にimportしてもよいが、core-js/stage
配下にstage-xのpolyfillをまとめたものが配置されているので、これを使うと楽。
例えばwebpack.config.jsで使う
entry: { client: ['@babel/polyfill', 'core-js/stage/2', './src/clientLoader.js'], },