お知らせ

現在サイトのリニューアル作業中のため、全体的にページの表示が乱れています。
投稿日:
ライブラリ::React開発::設計

別にReactに限った話でもないのですが、実務で悩ましいコードにあたって頭を抱えてる内にハゲてきたので、ハゲがこれ以上進まない事を祈り書きました。いやいっそのこと全ハゲになりたいですが、それはさておき
とりあえず個人的には以下2点を意識すれば起きないとは思うので、Next.jsを使った簡単なサンプルを交えながら3例ほどケーススタディ形式で紹介していきます

  • 密結合にしない
  • シンプルに実装する

挙げている事例については例示用にフルスクラッチで書いています(主題と関係ないコードは端折ってます
ここはこうした方がより良いのでは?などのご意見があれば是非コメントいただけると嬉しいです

コンポーネントのOptional引数のオーバーライド

コンポーネント引数をコンポーネント側で書き換えるような実装はどうかなと思います

問題点

  • 親の預かり知らぬところで値が設定されている
    • 誰かが知らずに設定値をすり替えたりしたら不具合が起きそうです
  • 型のコメントと設定値が異なる
    • 型のコメントと実装が異なるので、誰かが直すかもしれません
      • すると、コンポーネントを参照している全コンポーネントに影響が波及します
    • そもそも型と実装は本質的に関係ないので、こういった運用はNG

一例

type AccountContainerProps = {
  email: string;
  // デフォルトtrue
  isUpdate?: boolean;
  // デフォルトfalse
  gotNotify?: boolean;
};


export const AccountContainer = ({
  isUpdate = true,
  gotNotify = true,
  ...props
}: AccountContainerProps) => {
  ...
}

改善案

呼び元で明示的に指定するように変更しています
これによって親は子の実装を知る必要がなくなり、責務がそこで閉じるようになりました

改善点

  • 親の預かり知らぬところで値が設定されなくなった
    • これで子コンポーネントで値が変わることに怯える必要はなくなりました
  • 型の初期値コメントを削除できた
    • 実装と一致する保証がないコメントは削除するべきでしょう
type AccountContainerProps = {
  email: string;
  isUpdate: boolean;
  gotNotify: boolean;
};


export const AccountContainer = (props: AccountContainerProps) => {
  ...
}

コンポーネントのOptional引数の多重オーバーライド

コンポーネントのOptional引数のオーバーライドが多重化されている上に、なんか途中で更に書き換えられているとかいう地獄
どうしてそんなことをするのか…

一例

type AccountTemplateProps = {
  email: string;
  // デフォルトtrue
  isUpdate?: boolean;
  // デフォルトfalse
  gotNotify?: boolean;
  from: 'register' | 'update';
};

export const AccountTemplate = ({
  isUpdate = false,
  gotNotify = true,
  ...props
}: AccountTemplateProps) => {
  if (props.from === 'register') isUpdate = false;
  return <AccountContainer {...props} />;
};
type AccountContainerProps = {
  email: string;
  // デフォルトtrue
  isUpdate?: boolean;
  // デフォルトfalse
  gotNotify?: boolean;
};


export const AccountContainer = ({
  isUpdate = true,
  gotNotify = true,
  ...props
}: AccountContainerProps) => {
  ...
}

改善案

前項のように明示的に値を渡してあげるようにしましょう

直列に分散されたコンポーネント

コンポーネントの中にコンポーネントがネストされ続けてるパターンです
一見して何をしているのか分かりづらい上、StateやらHookやら色んな処理が各コンポーネントに分散配置されていることもあります

問題点

  • 親からみると子が何をしているのかが分かりづらい
  • コンポーネント名が意味をなしていない(責務が別れていない)
  • 子コンポーネントが状態を持っているため、親コンポーネントでハンドリングができない
  • 子コンポーネントのロジック変更が参照している全コンポーネントのロジックに波及する

一例

親コンポーネント

RegisterHeaderなる物が差し込まれていることだけがわかります
このコンポーネントが何をするのかはパッと見ではよくわかりません

const AccountUpdatePage = () => {
  return <Register header={<RegisterHeader />} />;
};
ラッピングしているコンポーネント

ラッパーなのでこのコンポーネントそのものは何をしているのかわかりません

type RegisterProps = {
  header: JSX.Element;
};

export const Register = (props: RegisterProps) => {
  return <>{props.header}</>;
};
差し込まれているコンポーネントの中身

どうやらRegisterHeaderRegisterContentを含むようです
なんでヘッダーの中にコンテンツがあるのでしょうか…

export const RegisterHeader = () => {
  return (
    <>
      <p>head</p>
      <RegisterContent />
    </>
  );
};
差し込まれているコンポーネントの子

現在のパスに応じて叩くAPIを変えるような実装がされていますが、もしパスが変わったり増えたりしたらこのコンポーネントの実装を知らない限り面倒なことになります

export const RegisterContent = () => {
  const rt = useRouter();
  const currentPath = rt.pathname;
  const url =
    currentPath === 'register'
      ? 'https://example.com/kaiin/touroku'
      : 'https://example.com/kaiin/koshin';
  const [username, setUsername] = useState<string | undefined>(undefined);

  const onSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
    ev.preventDefault();
    axios.post(url, {
      username,
    });
  };

  return (
    <>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          value={username}
          onChange={(ev) => setUsername(ev.target.value)}
        />
        <button>送信</button>
      </form>
      <RegisterFooter />
    </>
  );
};
export const RegisterFooter = () => {
  return <p>foot</p>;
};

改善案

改善点

  • 親から子への見通しが改善された
  • コンポーネント名が名前の通りの意味を持つようになった
    • ヘッダーはヘッダー、フォームはフォーム、フッターはフッターの責務だけに集中できます
    • Registerとか言う謎コンポーネントも姿を消しました
  • 状態をすべて親に集約した
  • 子コンポーネントのロジック変更が起きる確率が減った
    • 子コンポーネント側のロジックを減らし、親から渡されるコールバックで行うようにしたため、子コンポーネントのロジック変更が他に影響する確率が減りました
親コンポーネント
  • ひとまずヘッダーと入力フォーム、フッターがあるんだなという事が解るようにはなったと思います
  • 状態を親に集約したのでAPI叩く時のURIも態々パスから判断しなくて良くなったのでコードの複雑性が減っています
const usePageState = () => {
  const [username, setUsername] = useState('');

  return {
    username,
    setUsername,
  };
};

const onSubmit = (username: string) => {
  axios.post('https://example.com/kaiin/koshin', {
    username,
  });
};

const AccountUpdatePage = () => {
  const ps = usePageState();

  return (
    <>
      <RegisterHeader />
      <RegisterForm
        onChangeUsername={ps.setUsername}
        username={ps.username}
        onSubmit={() => onSubmit(ps.username)}
      />
      <RegisterFooter />
    </>
  );
};
ヘッダー
export const RegisterHeader = () => {
  return <p>head</p>;
};
入力フォーム
type RegisterFormProps = {
  username: string;
  onChangeUsername: (value: string) => void;
  onSubmit: () => void;
};

const onChangeUsername = (
  ev: React.ChangeEvent<HTMLInputElement>,
  setState: (value: string) => void
) => {
  const value = ev.target.value;
  setState(value);
};

const onSubmit = (
  ev: React.FormEvent<HTMLFormElement>,
  onSubmit: () => void
) => {
  ev.preventDefault();
  onSubmit();
};

export const RegisterForm = (props: RegisterFormProps) => {
  return (
    <>
      <form onSubmit={(ev) => onSubmit(ev, props.onSubmit)}>
        <input
          type="text"
          value={props.username}
          onChange={(ev) => onChangeUsername(ev, props.onChangeUsername)}
        />
        <button>送信</button>
      </form>
    </>
  );
};
フッター
export const RegisterFooter = () => {
  return <p>foot</p>;
};

突如としてPhpStormがクラッシュし、文字入力がまともにできなくなり、多くのアプリが起動不能に陥ったのでその対処法のメモです

環境

環境 内容
OS Windows 11 Pro Build 22000.469
M/B ASUS ROG STRIX Z390-F GAMING
VGA ROG-STRIX-RTX2060-O6G-GAMING

確認した事象

確認時刻:2022-01-28T22:38:00+09:00

  • Ctrl + Alt + Fを押したらPhpStormが落ちる
    • これがすべての始まり
  • PhpStormを再起動するとスプラッシュスクリーンが表示されたあと特に何も起きない
    • タスクマネージャーから見るとゾンビプロセスになってる
      • [f:id:lycolia:20220406231148p:plain]
  • 文字入力がまともにできない
    • 2秒おきくらいの感覚でフォーカスが飛ぶ
  • Thunderbirdがクラッシュし、再起動でゾンビプロセスになる
  • Docker Desktopがクラッシュする
  • ASUS GPU TweakIIがクラッシュする
  • イベントビューワがクラッシュする
  • Chromeはクラッシュしない
  • Explorerはクラッシュしない
  • OS再起動でも変わらず
  • セーフモードでは起きない
  • 次のコマンドで改善しない
    • sfc /scannow
    • Dism /Online /Cleanup-Image /CheckHealth
    • Dism /Online /Cleanup-Image /ScanHealth

TextInputHost.exeが無限増殖している様子

減ったり増えたりを繰り返すのですが、明らかに様子がおかしいです

TextInputHost.exeが無限増殖している様子

イベントビューワの様子

すべての始まりのトリガーらしきもの

増殖のトリガーらしきイベント

大量の警告KSR データを読み取ることができませんでした

大量の警告 KSR データを読み取ることができませんでした

大量のDCOM 1084エラー

大量のDCOM 1084エラー

改善した方法

  • ASUS GPU TweakIIのアンインストール
  • このアプリのexeファイルをリネームするだけでも改善します

後書き

  • ASUS GPU TweakII自体は2年以上前にインストールしていたので、なぜ突如こんな事になったのかはよくわかっていません
  • 取り敢えずPhpStormもThunderbirdも正常動作してますし、文字入力にも支障がなくなったので良かったです

参考にした記事

投稿日:
開発::自動化サービス::GitHub

Issueの中にあるTasksの進捗状況集計したい。したくない?したいですよね!
実はGitHubのREST APIを叩いてもTasks情報を素直に取れません
仕方がないので雑に取得するためのシェルスクリプトを書きました

自作するまでの経緯

GH CLI では出来ない

GitHub CLI使ったら簡単に出来るやろ、そう思っていた時代が私にもありました
なんとできません
検索クエリ投げないとProjectで絞れないようなかったり、痒いところに手が届かない子ですし、まぁ許しましょう

REST APIでもできない

でも REST APIなら流石に…なかった

ちょっと待ってほしい、画面上表示されてるのにAPIとしては提供されてないの?
そんなのある?

そんなデータを提供している機能はない

2016年には問題提起されていたようですが絶賛放置中

仕方ないので自作した

API叩いてIssue本文からTasksの情報を雑に抜いて集計するだけのやつです
超雑なのでインデントとかコードフェンスの中とか何も考慮してません