React
ドキュメントが結構おもしろい
チュートリアルっぽい、state をどこに置くのか等の話題
React コンポーネントの型
よく忘れる
type ReactChild = ReactElement | ReactText
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
雑に返り値の型を書く時はたいてい ReactElementで、ReactNode を指定したい時はほぼないはず
ComponentType
ComponentProps<typeof HogeComponent> Props の型取れる
型のパターン
テスト
TypeScript での型
children を受け取るコンポーネントの型
props: { children: ReactNode }
大抵これでよい, ReactNode が null や undefined をとりうるので ? はなくていい
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
既存の interface に合成したくなったら PropsWithChildren で拡張する
type PropsWithChildren<P> = P & { children?: ReactNode };
children を特定のコンポーネントに制限したい場合
たとえば List の children は 0 個以上の ListItem であってほしい時
code:childrentype.ts
type ListProps = {
children: ReactElement<typeof ListItem> | ReactElement<typeof ListItem>[];
}
styled-componet や @emotion/styled で既存のコンポーネントを拡張する
なんか型がつかなくない? と思ったら as typeof HogeComponent でオリャっと合わせにいく
code:styled_type.tsx
import styled from "@emotion/styled";
import { ListItem as MUIListItem } from "@material-ui/core";
const ListItem = styled(MUIListItem)`
border: dashed 1px;
margin: 5px;
` as typeof MUIListItem;
...
Component を props で渡すあれこれ
普通に children で渡しにくいことをしたい場合に
ComponentType を渡すなら createElement が使える、props の型は型パラメータとして渡す
code:pass_component.tsx
const PassType = (props: { childName: React.ComponentType }) => {
return (
<>
{React.createElement<ChildComponentProps>(props.childName, { propName: propValue })}
</>
);
}
Element を受け取る場合は cloneElement で props 拡張できる、key や ref もいい感じになる
code:pass_component.tsx
// 雑
const PassComponent = (props: { childElement: Element }) => {
return (
<>
{React.cloneElement(..., { ...props })}
</>
);
}
createContext のデフォルト値を省略したい
null 受け入れるようにするしかない?
code:context-default-value.tsx
interface MyContextState { ... }
export const MyContext = React.createContext<MyContextState | null>(null);
render 内の bind 避けしぐさ
render() 内で this.method.bind(this) すると再描画が都度おきるのでどうするんかなと
1. constructor 内で bind して代入する
code:bind-this.tsx
constructor(props: MyProps) {
super(props);
this.method = this.methid.bind(this);
}
method() {
...
}
冗長だし constructor 省略できるところでも必要になるのが微妙
2. public method = () => { ... } と変数フィールドのように書いていく
みんなそうしてるのかな? public のようなアクセス修飾子は省略したいけど、こうなるとなんか書きたく思えてしまう
useEffect 等で async/await 使う
code:async.jsx
useEffect(() => {
const f = async () => {
const foo = await ...
}
return f();
})
useEffect(async () => ...) は useEffect に Promise を返してしまう
useReducer
よくあるサンプルではカウンターみたいな感じであまりピンとこない
ちょっと仕事で使ってみたらありがたい気持ちになってきた
reducer はテストしやすい
似てるけど挙動の異なるコンポーネントを作り分けやすい
reducer だけ切り替えたり
(props: DateTimeRangeInputProps & { reducer: React.Reducer<State, Action> }) => {...}
別の reducer を呼んで追加の処理をして返す state をいじったり
複数の子コンポーネントから親に更新を通知したい場合 dispatch を渡すと楽
useState で返る setter を個別に渡していくのはダルい
非同期でデータとってくる
useEffect から dispatch を呼ぶ
キャンセル等を考えるとちょっとめんどいが十分できる
HOC の TypeScript での型
わすれるので思い出し用
code:hoi.ts
// 既存の Component に props.loading を追加するやつ(よくあるサンプル)
// 追加する props の interface
// 拡張する対称 Component の Props 型と合成する
interface WithLoadingProps {
loading: boolean;
}
// 拡張して新しいコンポーネントを返す関数
const WithLoading =
// P は Porps を表す型パラメータ
// P extends object でもいいし, 対象のコンポーネントの Props を制約したいならそれを基底にする
<P extends {}>
// 引数、他にオプション取りたいならそれも受け取る
// React.ComponentType<P> を受け取り、 WithLoadingProps を拡張した Props を取るコンポーネントを返す
(Component: React.ComponentType<P>): React.ComponentType<P & WithLoadingProps> =>
// WithLoadingProps で束縛する値はここで spread しておくと楽、返り値の気持ちが tsc に伝わらないときは怒られるので、
// const NewComponent: React.ComponentType<P & WithLoadingProps> = ... とかに代入する
({ loading, ...props }) => {
// ... 他に何かコンポーネントでやりたいことあるだろう
return loading ? <LoadingIndicator /> : <Component {...props as P} />;
};
// 拡張する
const HogeWithLoading = WithLoading(Hoge)
// propType とかでごちゃごちゃ言われるときは
// WithLoading(Hoge as React.ComponetType<HogeProps>) などで黙ることもある
Material-UI
これで css の挿入先を最初にして、上書き可能にしておくとよい
useEffect 2回のやつ
render hooks パターン