React
Hello World
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
</body>
<script src="./index.tsx"></script>
</html>
index.tsx
import * as React from "react";
import * as ReactDOM from "react-dom";
ReactDOM.render(<p>Hello World!!!</p>, document.getElementById("app"));
便利なもの
clsx
https://www.npmjs.com/package/clsx
yarn add clsx
使用例。
visible=true
の場合はクラスにvisible
が追加される。
import clsx from "clsx";
interface Props {
visible: boolean;
}
const AppMenu: React.FunctionComponent<Props> = props => {
const { visible } = props;
return <div className={clsx("app-menu", { visible })}>{props.children}</div>;
};
classnames
boolean
型のtrue
, false
によってスタイルを設定するときに簡単に書けるようになる。
yarn add classnames
const classNames = require("classnames");
React Router
React Hooks
useEffect で非同期処理(async/await)を実行する
useEffect
をラップしたカスタムフックを作ればいい。
import { useEffect, DependencyList } from "react";
export default function useEffectAsync(
effect: () => any,
deps?: DependencyList
) {
useEffect(() => {
effect();
}, deps);
}
useEffectAsync(async () => {
// 初期マウント時に一度だけデータを非同期で取得する
const items = await fetchSomeItems();
console.log(items);
}, []);
Ref: https://stackoverflow.com/a/54637708
最初のマウント時のみ処理する
useEffect
の第二引数に[]
を指定することで再描画時の処理はせず、最初の描画時のみ処理ができる。
イベントリスナの登録などに便利。
useEffect(() => console.log("initial mounted."), []);
現在の state を非同期処理の中でアクセスする
useEffect
でコンポーネントマウント時にaddEventListener
し、その処理の中で state にアクセスしたい場合は、useRef
を使う。
以下のようにカスタムコンポーネント化すると便利。
const useRefState = <T>(
initialValue: T
): [T, React.MutableRefObject<T>, React.Dispatch<React.SetStateAction<T>>] => {
const [state, setState] = useState<T>(initialValue);
const stateRef = useRef(state);
useEffect(() => {
stateRef.current = state;
}, [state]);
return [state, stateRef, setState];
};
5 秒間カウントアップしながらアラートを表示する場合の例。
const component = () => {
const [count, countRef, setCount] = useRefState(0);
useEffect(() => {
setTimeout(() => {
alert("Value: " + counterRef.current);
}, 5000);
}, []);
return <div>{count}</div>;
};
Ref: https://blog.castiel.me/posts/2019-02-19-react-hooks-get-current-state-back-to-the-future/
描画後に DOM に対して処理
普通にuseRef
を使う例。
const component = () => {
const ref = useRef();
const handleClick = () => console.log(ref.current);
return (
<p ref={ref} onClick={handleClick}>
hoge
</p>
);
};
しかし、useRef
とuseEffect
を組み合わせて、**初期描画時に 1 度だけ DOM に対して処理(css クラスの追加など)**をしたい場合はこの方法が使えない。
理由はuseEffect
のタイミングではref
が null になる場合があるため。
そういう場合はuseCallback
を使う。
const component = () => {
const applyClass = useCallback(node => {
if (node != null) {
const el = node as any;
el.classList.add("new-class");
}
});
return <p ref={applyClass}>hoge</p>;
};
Ref: https://github.com/facebook/react/issues/14387#issuecomment-493677168
インストール
yarn add next react react-dom
# TypeSciriptを使う場合は追加で入れる
yarn add -D @types/next
ルール
pages
ディレクトリの中にあるコンポーネントは自動で URL と紐づけられる
pages
- Index.jsx
- About.jsx
- contents
- ContentA.jsx
この場合、以下のページが存在することになる。
localhost:3000/index
localhost:3000/about
localhost:3000/contents/contentA
共通コンポーネントや util 的なコンポーネントはpages
以外に置く。
スニペット
input[type="text"]の値を state に保持する
export default () => {
const [state, setState] = useState("");
return <input value={state} onChange={e => setState(e.target.value)} />;
};
DOMのカスタム属性(data-***)を利用する
const Component = () => {
const [hoge, setHoge] = useState('aaa')
return <div className='my-component' data-hoge={hoge}></div>
}
.my-component {
&[data-hoge='aaa']{
background: red;
}
}
useStateで関数を管理する
関数をそのままuseState
の引数に渡すとFunctionalUpdate扱いされてしまうのでオブジェクトとして渡す。
const [func setFunc] = useState({fn: () => 'hoge'})
// 使い方
func.fn()
setFunc({fn: () => 'fuga'})
Ref: https://qiita.com/terrierscript/items/6a8acbc7d1ce6521f879
カスタムフックにすると便利。
const useStateF = <T extends (a: any) => any>(initialState: T): [T, (fn: T) => void] => {
const [f, setF] = useState<{ fn: T }>({ fn: initialState })
return [f.fn, fn => setF({ fn })]
}
// 使い方
const [myFunc, setMyFunc] = useStateF<() => void>(() => console.log('hello'))
myFunc()
setMyFunc(() => console.log('world))
props.childrenについて
汎用的なコンテナコンポーネントを作る場合にchildren
を利用できる。
関数としてchildrenを渡す
Ref: A deep dive into children in React - Max Stoibers Blog
コンポーネント作成
ひな形
import * as React from "react";
interface Props {
label: string;
onClick?: () => void;
}
const Button: React.FunctionComponent<Props> = props => {
return (
<div className="button" onClick={props.onClick}>
{props.label}
</div>
);
};
export default Button;