エンジニアの覚え書き

web系エンジニアの技術メモを主に投稿していきます。

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_limitresource → deploy配下に移った
  • extendsは現時点ではversion3でサポートされてないけど、https://github.com/moby/moby/issues/31101次第でサポート再開されるかも
  • group_addはなくなった
  • link_local_ipspids_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.jsconfig.js@babel/polyfillをimportするようにした。

github.com

※issueの内容自体はbabel7と関係ないけど、これで上手くいったので引用。

// .storybook/addons.js, .storybook/config.js
import '@babel/polyfill';

lint

TypeError: Cannot read property 'range' of nullになるので、eslintの設定を.eslintrcに切出しして、一時的にindenttemplate-curly-spacingをoffにした。

github.com github.com

    "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の設定を参照するようにした。

github.com

// babel.config.js
const pkg = require('./package.json');

module.exports = {
  presets: pkg.babel.presets,
  env: pkg.babel.env,
  plugins: pkg.babel.plugins,
};

flexboxについてのメモ

毎回chromeのdevtoolsで期待した見た目になるように試行錯誤しつつプロパティ設定していて時間を取られていたので、調べてメモ書きしておく。主軸の概念とか知らなかったので想定より勉強になった(知識が穴だらけで悲しい)。

参考

これを読んでおけばOK。

developer.mozilla.org

プロパティについて

  • flex-direction…主軸の向きと方向を指定
    • flex-direction: row:の場合、主軸は水平(→)
    • flex-direction: column:の場合、主軸は垂直(↓)
  • justify-content…主軸に沿った向きのレイアウトを指定
  • align-items…主軸に直行した向きのレイアウトを指定

Babel7のpolyfillのこれまでと違う点のメモ

babel7から@babel/polyfillにproposal polyfill(stage-3以下のpolyfill)が含まれなくなり、core-jsからのimportを個別に行う必要があると知ったのでメモしておく。

babeljs.io

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'],
  },