1. Flutter プロジェクトの作成 #
flutter create -i swift -a kotlin my_flutter_app
その後、Android エミュレータと iOS シミュレータを立ち上げてカウンターアプリが表示されることを確認する。
...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;
ネットワークエラー等で画像が取得できなかったときは、自動でフォールバック用の画像を設定してくれます。
...こんな配列があるとして、
const items = ['hello', null, 'world'];
こうしても、残念ながら型はうまくフィルタリングされません。
const filtered = items.filter((item) => item !== null);
// filtered: (string | null)[]
ですので、Type Guard を使ってこうするのが一般的だと思うのですが、
...なんとなく MDN で JavaScript の式と演算子の一覧を眺めていたら目に入ったのが「カンマ演算子(,)」です。恥ずかしながら今回初めて知りました。
しかし実は知らずに使っていただけで、 for 文に出てくるのがそれでした。(for 文をこのように使うこともそれほど多くはないですが。)
...例えば Window.alert()
のように、ユーザがボタンをクリックするまでの間、処理を止めたい場面があったとします。
このとき、パッと思いつくのは、
です。(間違いなく後者を採用すべきです。)
...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:
...
本記事では React コンポーネントを用いて説明していますが、本記事の内容は通常の HTML / CSS だけで実現可能です。
text-overflow: ellipsis;
を機能させるためには以下の CSS をすべて指定する必要があります。
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
というメソッド名にしています。
ジェネリック型を引数にとる関数の戻り値の型を取得すると、結果は以下のようになります。
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
実際には渡される引数によってさらに具体的な型になるわけですが、このように戻り値の型を定義しておこうとすると、ジェネリックな部分についてはどうしても型に反映できなくなります。
...こんな型があるとします。
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 なプロパティのみを抽出するならばこうです。
...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 <></>;
}
コンソールには以下の順番で出力されます。
undefined
2
初回レンダリングは undefined となっているとして、次のレンダリングでは最後に指定された setState の結果が有効となるようです。
...関数の実行結果として別の関数が return されるようなケースがあります。
例えばイベントリスナーに処理を登録して、その結果としてリスナーを解除するための関数が return されるといった感じですね。外部のライブラリを使用していると遭遇することも多いと思います。
...コールバック Ref パターンがあまり使われていない(知られていない?)ように思われたので布教も兼ねて。
import { useRef, useEffect } from 'react';
function Component() {
const ref = useRef();
useEffect(() => {
ref.current.innerHTML = 'Hello, world.';
}, []);
return <div ref={ref} />;
}
import { useCallback } from 'react';
function Component() {
const setRef = useCallback((node) => {
node.innerHTML = 'Hello, world.';
}, []);
return <div ref={setRef} />;
}
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} />;
}
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} />;
}
function Component() {
return <div ref={(ref) => console.log(ref)} />; // <div></div>
}
コールバック Ref
...
useEffect
の return
など、コンポーネントのアンマウント時に実行される処理ですが、これはページを再読み込みした時には実行されません。
リロード(だったり、アドレスバーに現在と同じ URL を指定して開いたり)はつまり、一度ページを閉じて新しく開くようなものです。
...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
挨拶文言を定義する型があったとします。
type Greet = 'hello' | 'goodbye';
const greet: Greet = 'hello';
文言には hello と goodbye がよく使われるものの、これだけに固定される必要はなく、話者が好きな言葉を使用して良いものとします。
...例えばスタイリングされたパーツのコンポーネントを作るとして、ある HTML 属性をプロップスで受け取るようにする場合、これまでは MDN のドキュメントを見て手打ち入力していました。
...example.com
でサイトを運営していた。example.com
は other.example.com
としてドメインを変更して引き続きサイトを運営しつつ、example.com
には新たなサイトを割り当てることにした。example.com
を other.example.com
にリダイレクトさせるようにしておいた。example.com
を新たなサイトに割り当てた。example.com
にアクセスすると other.example.com
にリダイレクトされてしまう。【注意喚起】Google Chrome は 301 リダイレクトを永久キャッシュするので要注意
...
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 している。
...例えばフロントエンドとバックエンドの両方を TypeScript で開発している場合、型定義を共有したい場面があるはず。
両方に同じコードをコピペすることで当座は解決はできますが、長期的に考えるのであれば同じ1つのファイルを参照するようにしておくべきです。
...「難しい」というのは実装が難しいということではなく、UX の観点から本当に採用すべきか考えた方が良いケースもあるという意味です。
なおこれは Web アプリに限った話なので、ネイティブアプリには関係がありません。
...Google Chrome のシークレットモード(インコグニトモード)では Firebase の signInWithRedirect
と signInWithPopup
が機能しません。
最も遭遇しやすいであろう環境を想定して Google Chrome と記載しましたが、他のブラウザでも発生します。
...Firebase で Google や Facebook といった外部認証を利用したログインを実施する場合 signInWithPopup
または signInWithRedirect
のメソッドを利用します。
参考:https://firebase.google.com/docs/auth/web/google-signin
大抵の場合 UI を独自に構築したいでしょうから signInWithRedirect
の方を利用することになるでしょう。
日付を表す文字列があるとして、可能ならばこれを Date オブジェクトに変換したい、というような時に使える関数です。
/** 引数を 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;
};
引数の型から Date 変換できないと判断できるものは先に弾くのか、それとも JavaScript 版のようにとりあえず全ての引数を受け付けるのか、どのように使いたいかにより、2パターンに分けられます。
...