March 6, 2022
こんなのどうでしょう?
#
const fallbackImage = '...';
type Props = {
src: JSX.IntrinsicElements['img']['src'];
alt?: JSX.IntrinsicElements['img']['alt'];
srcOnError?: JSX.IntrinsicElements['img']['src'];
className?: JSX.IntrinsicElements['img']['className'];
};
function Component(props: Props) {
const { src, alt, srcOnError, className } = props;
const srcFileName = src?.split('/').pop()?.split('.').shift();
return (
<img
className={className}
src={src}
alt={alt || srcFileName}
onError={(e) => {
e.currentTarget.onerror = null;
e.currentTarget.src = srcOnError ?? fallbackImage;
}}
/>
);
}
export const Image = Component;
嬉しい点1:エラー時のハンドリング
#
ネットワークエラー等で画像が取得できなかったときは、自動でフォールバック用の画像を設定してくれます。
...
March 3, 2022
iOS シミュレータ
#
シミュレータを起動する
#
open -a Simulator
または
open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app
シミュレータからアクセスする
#
シミュレータ上で Safari を開く。
例えば localhost:5500
で起動しているサーバにアクセスしたいなら Safari の URL 入力欄に localhost:5500
を入力すればアクセスできる。
...
March 2, 2022
本格的なアプリを作成する場合は Sentry などのサービスを使うことが多いと思います。
そこまでは必要ないけれど、エラーログの仕組みをマニュアルで簡易的に作成してきたい時の作り方。
...
March 1, 2022
こんな配列があるとして、
const items = ['hello', null, 'world'];
こうしても、残念ながら型はうまくフィルタリングされません。
const filtered = items.filter((item) => item !== null);
// filtered: (string | null)[]
ですので、Type Guard を使ってこうするのが一般的だと思うのですが、
...
February 28, 2022
なんとなく MDN で JavaScript の式と演算子の一覧を眺めていたら目に入ったのが「カンマ演算子(,)」です。恥ずかしながら今回初めて知りました。
しかし実は知らずに使っていただけで、 for 文に出てくるのがそれでした。(for 文をこのように使うこともそれほど多くはないですが。)
...
February 27, 2022
例えば Window.alert()
のように、ユーザがボタンをクリックするまでの間、処理を止めたい場面があったとします。
このとき、パッと思いつくのは、
- ビジーウェイトする方法
- Promise の解決を待つ方法
です。(間違いなく後者を採用すべきです。)
...
February 26, 2022
Redux はもう使っていないが、ソースコード配置の考え方はどのプロジェクトでも役に立っているのでここにメモ。
Since Redux is just a data store library, it has no direct opinion on how your project should be structured. However, there are a few common patterns that most Redux developers tend to use:
...
February 25, 2022
本記事では React コンポーネントを用いて説明していますが、本記事の内容は通常の HTML / CSS だけで実現可能です。
text-overflow: ellipsis;
を機能させるためには以下の CSS をすべて指定する必要があります。
...
February 24, 2022
this
を返すようにするとメソッドチェーンで書けるので便利
#
class Calculator {
#number;
constructor(initial = 0) {
this.#number = initial;
}
print() {
console.log(this.#number);
}
add(num) {
this.#number += num;
return this;
}
subtract(num) {
this.#number -= num;
return this;
}
multiply(num) {
this.#number *= num;
return this;
}
divide(num) {
this.#number /= num;
return this;
}
}
new Calculator(9).add(5).subtract(4).multiply(3).divide(2).print(); // 15
new
を書きたくないならファクトリーメソッドを用意すると便利
#
以下では of
というメソッド名にしています。
...
February 23, 2022
ジェネリック型を引数にとる関数の戻り値の型を取得すると、結果は以下のようになります。
function foo<T>(arg: T) {
return arg;
}
function bar<T extends object>(arg: T) {
return arg;
}
type FooReturn = ReturnType<typeof foo>; // unknown
type BarReturn = ReturnType<typeof bar>; // object
実際には渡される引数によってさらに具体的な型になるわけですが、このように戻り値の型を定義しておこうとすると、ジェネリックな部分についてはどうしても型に反映できなくなります。
...
February 22, 2022
こんな型があるとします。
type Person = {
name: string;
age: number;
isAdult: boolean;
siblings: string[];
birthplace?: string;
height?: number;
hasPartner?: boolean;
friends?: string[];
};
こうすれば Optional なプロパティのみを抽出できます。
type OptionalPropertyKeys<T> = {
[K in keyof T]-?: undefined extends T[K] ? K : never;
}[keyof T];
type OptionalPersonPropertyKeys = OptionalPropertyKeys<Person>;
// 👉 "birthplace" | "height" | "hasPartner" | "friends"
type OptionalPersonProperty = Pick<Person, OptionalPersonPropertyKeys>;
// 👉 {
// birthplace?: string;
// height?: number;
// hasPartner?: boolean;
// friends?: string[];
// }
/**
* ということでこうして使いましょう。
*/
type OptionalProperty<T> = Pick<
T,
{
[K in keyof T]: undefined extends T[K] ? K : never;
}[keyof T]
>;
逆に、Required なプロパティのみを抽出するならばこうです。
...
February 21, 2022
試行その1
#
import { useState, useEffect } from 'react';
export default function Component() {
const [int, setInt] = useState<number>();
useEffect(() => setInt(1), []);
useEffect(() => setInt(3), []);
useEffect(() => setInt(2), []);
console.log(int);
return <></>;
}
結果その1
#
コンソールには以下の順番で出力されます。
初回レンダリングは undefined となっているとして、次のレンダリングでは最後に指定された setState の結果が有効となるようです。
...
February 20, 2022
関数の実行結果として別の関数が return されるようなケースがあります。
例えばイベントリスナーに処理を登録して、その結果としてリスナーを解除するための関数が return されるといった感じですね。外部のライブラリを使用していると遭遇することも多いと思います。
...
February 19, 2022
コールバック Ref パターンがあまり使われていない(知られていない?)ように思われたので布教も兼ねて。
1. DOM のアタッチ時だけ処理が必要な場合
#
1-1. useRef + useEffect パターン
#
import { useRef, useEffect } from 'react';
function Component() {
const ref = useRef();
useEffect(() => {
ref.current.innerHTML = 'Hello, world.';
}, []);
return <div ref={ref} />;
}
1-2. useCallback パターン
#
import { useCallback } from 'react';
function Component() {
const setRef = useCallback((node) => {
node.innerHTML = 'Hello, world.';
}, []);
return <div ref={setRef} />;
}
2. DOM のアタッチ後にも処理が必要な場合
#
2-1. useRef + useEffect パターン
#
import { useRef, useEffect } from 'react';
function Component() {
const ref = useRef();
useEffect(() => {
ref.current.innerHTML = 'Hello, world.';
}, []);
const handleOnClick = () => {
ref.current.innerHTML = 'Goodbye, world.';
};
return <div ref={ref} onClick={handleOnClick} />;
}
2-2. useRef + useCallback パターン
#
import { useRef, useCallback } from 'react';
function Component() {
const ref = useRef();
const setRef = useCallback((node) => {
node.innerHTML = 'Hello, world.';
ref.current = node;
}, []);
const handleOnClick = () => {
ref.current.innerHTML = 'Goodbye, world.';
};
return <div ref={setRef} onClick={handleOnClick} />;
}
FYI:コールバック Ref
#
function Component() {
return <div ref={(ref) => console.log(ref)} />; // <div></div>
}
コールバック Ref
...
February 18, 2022
useEffect
の return
など、コンポーネントのアンマウント時に実行される処理ですが、これはページを再読み込みした時には実行されません。
リロード(だったり、アドレスバーに現在と同じ URL を指定して開いたり)はつまり、一度ページを閉じて新しく開くようなものです。
...
February 17, 2022
Parent コンポーネントが ChildA および ChildB をインポートとしているとして、それぞれのコンポーネントで以下のように console.log()
しているとどの順番に出力されるのか。
Parent.jsx
import { ChildB } from './ChildB';
import { ChildA } from './ChildA';
console.log('Parent | Outer Top');
export function Parent() {
console.log('Parent | Inner');
return (
<>
<ChildA />
<ChildB />
</>
);
}
console.log('Parent | Outer Bottom');
ChildA.jsx
...
February 16, 2022
挨拶文言を定義する型があったとします。
type Greet = 'hello' | 'goodbye';
const greet: Greet = 'hello';
文言には hello と goodbye がよく使われるものの、これだけに固定される必要はなく、話者が好きな言葉を使用して良いものとします。
...
February 15, 2022
例えばスタイリングされたパーツのコンポーネントを作るとして、ある HTML 属性をプロップスで受け取るようにする場合、これまでは MDN のドキュメントを見て手打ち入力していました。
...
February 13, 2022
ハマった経緯
#
最初に
#
- これまで
example.com
でサイトを運営していた。
そして
#
- これまで運営していた
example.com
は other.example.com
としてドメインを変更して引き続きサイトを運営しつつ、
example.com
には新たなサイトを割り当てることにした。
そのため
#
- まずは
example.com
を other.example.com
にリダイレクトさせるようにしておいた。
- 一定期間たったあとでそのリダイレクトを解除し
example.com
を新たなサイトに割り当てた。
すると起きた問題が
#
- すでにリダイレクト設定を解除しているにも関わらず
example.com
にアクセスすると other.example.com
にリダイレクトされてしまう。
原因は
#
- リダイレクトでは 301 リダイレクトしていた。
- Google Chrome がこれをリダイレクトキャッシュとしてブラウザにキャッシュしているのが原因。
- このキャッシュ期限は永久で、意図的に削除しない限り残り続ける。
- (Google Chrome 以外に Firefox も同様に永久キャッシュするようです)
【注意喚起】Google Chrome は 301 リダイレクトを永久キャッシュするので要注意
...
February 12, 2022
事の発端(蛇足)
#
少し前の話にはなりますが、AWS の Lightsail で運用していたワードプレスサイトがある日突然 SSL 証明書のエラーで開けなくなってしまいました。
...
February 4, 2022
タイトルでうまいこと表現できませんでしたがつまりこういう状況です。
状況
#
GitHub Packages(や npmjs)に作った組織内のプライベートなパッケージを利用しているプロジェクトがあるとして、利用する際は .npmrc
でパッケージの在処とアクセストークンを指定している。
...
February 3, 2022
import の from にある /(スラッシュ) の意味
#
npm のパッケージを import する時に from の中でスラッシュを記述するものがある。例えば Firebase SDK はこのパターン。
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getFunctions } from 'firebase/functions';
これはサブディレクトリから import している。
...
February 2, 2022
例えばフロントエンドとバックエンドの両方を TypeScript で開発している場合、型定義を共有したい場面があるはず。
両方に同じコードをコピペすることで当座は解決はできますが、長期的に考えるのであれば同じ1つのファイルを参照するようにしておくべきです。
...
February 1, 2022
SPA の優位性(ポエム)
#
Angular や React をはじめとした CSR ライブラリが登場して以来、SSR が登場し、SSG が登場し、ISR が登場しました。
技術の登場順に従って、一番初めに登場した CSR よりもその後登場した技術の方が上位だと考える人もいるかもしれません。
...
January 31, 2022
Web アプリとマジックリンクの組み合わせはなかなか難しいと思った話。
(Firebase 使用中に遭遇したのでそれをベースに書いていますが Firebase に限った話ではありません)
...
January 30, 2022
「難しい」というのは実装が難しいということではなく、UX の観点から本当に採用すべきか考えた方が良いケースもあるという意味です。
なおこれは Web アプリに限った話なので、ネイティブアプリには関係がありません。
...
January 29, 2022
Google Chrome のシークレットモード(インコグニトモード)では Firebase の signInWithRedirect
と signInWithPopup
が機能しません。
最も遭遇しやすいであろう環境を想定して Google Chrome と記載しましたが、他のブラウザでも発生します。
...
January 28, 2022
Firebase で Google や Facebook といった外部認証を利用したログインを実施する場合 signInWithPopup
または signInWithRedirect
のメソッドを利用します。
参考:https://firebase.google.com/docs/auth/web/google-signin
大抵の場合 UI を独自に構築したいでしょうから signInWithRedirect
の方を利用することになるでしょう。
...
January 26, 2022
日付を表す文字列があるとして、可能ならばこれを Date オブジェクトに変換したい、というような時に使える関数です。
JavaScript 版
#
/** 引数を Date オブジェクトへ変換する。Date として解釈できなかった場合は引数をそのまま返す。 */
const tryParseToDate = (arg) => {
if (arg instanceof Date) {
return arg;
}
if (typeof arg !== 'string' && typeof arg !== 'number') {
return arg;
}
const maybeDate = new Date(arg);
const invalidDate = Number.isNaN(maybeDate.getTime());
return invalidDate ? arg : maybeDate;
};
TypeScript 版
#
引数の型から Date 変換できないと判断できるものは先に弾くのか、それとも JavaScript 版のようにとりあえず全ての引数を受け付けるのか、どのように使いたいかにより、2パターンに分けられます。
...
January 25, 2022
こんな感じで文字列を検索したい時に使える関数をご紹介します。
const names = [
'ほしの ユタカ',
'つきもと マコト',
'かざま リュウイチ',
'さくま マナブ',
'こん ウェンガ',
];
const res1 = search(names, 'ユタカ'); // 'ほしの ユタカ' が返ってきて欲しい。
const res2 = search(names, 'きもと'); // 'つきもと マコト' が返ってきて欲しい。
補足
...