ランサーズ(Lancers)エンジニアブログ > フロントエンド > フロントエンド定例 > Datadogのモニター分けた NodeJSバージョンアップした イベント発火とstate更新順 loading処理の難しさ フロントエンド定例 2022/7/15

Datadogのモニター分けた NodeJSバージョンアップした イベント発火とstate更新順 loading処理の難しさ フロントエンド定例 2022/7/15

igarashisho|2022年07月15日
フロントエンド

こんにちは、フロントエンドチームの @syo_igarashi です。
今週のフロントエンド定例の内容を記載します。

フロントエンド定例について、以前の記事(ランサーズのフロントエンドチームが取り組んでいること)でお伝えしたのですが、毎週金曜日に開催しており、実際の業務で取り組んでいることや気になった技術情報等をシェアしあう会になっています。

以下、今週の内容です。

Datadogのモニター分けた

@view.url_details.path:\/[対処のURLのパス]\/*

上記のログのクエリ追加してモニターの設定増やしてSlackの通知も分けるようにしました。
各実装者がDatadogのモニターを追加するやり方いいかもしれません。
まだ一部の箇所でしかログ収集していないので次回のアーキテクチャチームでも話してみてログ収集対応広げるのとモニターを増やしていくのか検討したいと思います。

共通で無視したいログのクエリ(ネットワークエラーなど)をどう管理するのがいいのかは悩み中です。

NodeJSバージョンアップした

フロントエンド定例 2022/6/10

過去に話していた差分を確認しながらバージョンアップを進める対応が完了しました。
静的ファイルを生成するプロジェクトのNodeJSバージョンが10 -> 16になりました。
一部削除予定だったり使われていないプロジェクトはそのままにしてます。

LambdaのNodeJS…まずはテストを書くところから始めないとなぁとふんわりと思ってます。

イベント発火とstate更新順

例えばイベントのタイミングで状態変更とAPIを読み込みをしようとする想定の
hook関数とJSXがあった場合、

const useHooks = () => {
  const [ text, setText ] = useState('')

  const onClick = useCallback(() => {
    setText('Hello')

    fetch(
      'htttps://~~~',
      {
         headers: {
           'content-type': 'application/json',
         },
         data: JSON.stringfy({
           // 初回のonClick起動のとき'Hello'ではなく''になる
           text
         });
      }
    );
  }, [ text ])

  return {
    onClick
  }
}

const Component = () => {
  const { onClick } = useHooks();

  return (
    <button type="button" onClick={onClick}>ボタン</button>
  )
}

なんとなくこんな感じなのを書くと思います。
ここで問題なのは同じイベントのタイミングでは状態変更してもイベントの関数内の状態を扱う変数自体は変更ない感じに初回はなってしまうことです。

これの解決方法としてHTMLのクリックで発火する関数が他にもあるので状態変更とその他の処理を分けてみる手も場合によってはあるのかなと思いました。
発火する順番として下記になります。
1 onmousedown -> 2 onblur -> 3 onmouseup -> 4 onclick

上記のコードを編集すると

const useHooks = () => {
  const [ text, setText ] = useState('')

  const onMouseDown = useCallback(() => {
    setText('Hello')
  }, [ text ])
  const onClick = useCallback(() => {
    fetch(
      'htttps://~~~',
      {
         headers: {
           'content-type': 'application/json',
         },
         data: JSON.stringfy({
           // ちゃんと'Hello'として送信される
           text
         });
      }
    );
  }, [ text ])

  return {
    onMouseDown,
    onClick
  }
}

const Component = () => {
  const { onMouseDown, onClick } = useHooks();

  return (
    <button type="button" onMouseDown={onMouseDown} onClick={onClick}>ボタン</button>
  )
}

という書き方になります。

実際の挙動の確認としてこちらでも検証できると思います。

https://codepen.io/igara/pen/oNqYXzM

 

ライブラリ的な解決方法もありそうですがObservableな値を派生させて使わせるようなComputedみたいなので解決させるやり方というのもありそうですよね。

loading処理の難しさ

ボタン押下したときに更新処理をREST APIに投げた際にダブルサブミット防止のためfetch処理前後にbooleanを扱う状態の更新をするときがあると思います。

const Component = () => {
  const [loading, setLoading] = useState(false)
  const onClick = useCallback{async() => {
    if (!loading) {
      setLoading(true)
      await fetch(...)
      setLoading(false)
    }
  }, [loading])

  return (
    <button type="button" onClick={onClick}>ボタン</button>
  )
}

コードにすると上記の様なものです。

SWR ではhook関数を使う感覚でAPIを取り扱うことが可能ですがhook関数なので更新などのイベントの処理で扱うことが難しいのかな?と思っています。
(それとは別でloadingを示すのがdata, errorのデータがないときがloadingの扱いという変数2つ確認しないとloadingの表現ができないのかというのも疑問もある)

ReactのAPIにあるSuspenseにも言えますが更新系の処理は実はclickとかのイベントでやるより、更新後に表示される想定のページのコンポーネント内で更新処理をさせるというのが案外勝手が良いというのがあるかもしれません。
別の画面の表示タイミングでAPI読み込む想定になるのでダブルサブミットによる複数回API読み込まれることはないですが、ページ更新で何度もAPIを読み込まれていないかとか心配する必要性は一定あります。

 

次回の更新予定は、7/22(金)になります!

前回の定例内容はこちらから確認可能ですのでご興味いただければ下記のリンクから閲覧いただければと思います。

https://engineer.blog.lancers.jp/?s=フロントエンド定例