🧲
[React] 初心者講座05 <状態管理(Recoil)の準備> (ついでにjson-server, axios)
以下を順にinstallして準備する。
-
Recoil
- +1, -1のcounterを表示する
-
json-server
- jsファイルからdb.jsonを生成する
-
axios
- json-serverからgetする
Recoil
install
$ npm install recoil
準備
src直下にstate
ファイルを作成する。
(stateのサブディレクトリもあった方が管理しやすそうだけど、今回はお試しなので無し)
- /src
- /state
- tmpCounterState.ts
- /views
- /state
tmpCounterState.ts
import { useCallback } from 'react';
import { atom, useRecoilValue, useSetRecoilState } from 'recoil';
// atomを定義
// これ自体はexportしない
const tmpCounterState = atom<number>({
// keyはファイル名と合わせる
key: 'tmpCounterState',
// 初期値
default: 0,
});
// stateの値を取得する関数
export const useTmpCounterState = (): number => {
return useRecoilValue(tmpCounterState);
};
// stateの値を'+1'する関数
export const useCountUpTmpCounterState = () => {
// stateの値を変更するset関数を取得
const setState = useSetRecoilState(tmpCounterState);
// '+1'する処理
const countUp = useCallback(() => {
setState((currVal) => currVal + 1);
}, [setState]);
return countUp;
};
// stateの値を'-1'する関数(上の関数とほぼ同じ)
export const useCountDownTmpCounterState = () => {
const setState = useSetRecoilState(tmpCounterState);
const countDown = useCallback(() => {
setState((currVal) => currVal - 1);
}, [setState]);
return countDown;
};
確認
DashboardView.tsx
const DashboardView: React.VFC = () => {
// 現在のstateの値を取得
const tempCount = useTmpCounterState();
// stateを'+1'する関数
const countUp = useCountUpTmpCounterState();
// stateを'-1'する関数
const countDown = useCountDownTmpCounterState();
return (
<Box>
<ViewTitleLabel label="Dashboard" />
{/* stateの値を表示 */}
<Box>{tempCount}</Box>
{/* stateの値を更新するButton */}
<Button variant={'outlined'} onClick={countUp}>
count+1
</Button>
<Button variant={'outlined'} onClick={countDown}>
count-1
</Button>
</Box>
);
};
ボタンをクリックすると数値が増減するようになった。
json-server
install
$ npm install json-server --save-dev
準備
package.json
"scripts": {
...
"server": "json-server --watch db.json --port 3001" // 追加
},
実行
$ npm run server
db.json
は作成していなくても勝手に作ってくれます。
\{^_^}/ hi!
Loading db.json
Oops, db.json doesn't seem to exist
Creating db.json with some default data
自動生成されるdb.json
は以下。
db.json
{
"posts": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
}
],
"profile": {
"name": "typicode"
}
}
確認
$ npm run server
が成功すると以下も表示されているはず。
Resources
http://localhost:3001/posts
http://localhost:3001/comments
http://localhost:3001/profile
Home
http://localhost:3001
http://localhost:3001/posts
にアクセス。
jsファイルからdb.jsonを生成
ダミーデータとはいえ、jsonファイルを編集して大量データを作るのは大変なので、jsファイルからdb.jsonを生成できるようにする。
npm scriptを同時実行すると楽なので、以下をinstallしておく。
$ npm install npm-run-all --save-dev
genDb.js
をpackage.json
と同じ階層に作成。
名前のランダム生成等はFaker.js
が便利でしたが、色々あったので数値つけるだけ。
for文の数値を増やせばdataを増やせるので一覧表示等が確認しやすくなる。
genDb.js
const db = {
sample: [],
};
for (let i = 0; i < 5; i++) {
db.sample.push({
id: i,
name: 'hoge' + i,
});
}
console.log(JSON.stringify(db));
package.json
を以下のように書き変えます。
run-s
はnpm scriptを順番に実行してくれます。
package.json
"scripts": {
...
"mock:genDb": "node genDb.js > db.json",
"mock:server": "json-server --watch db.json --port 3001",
"mock": "run-s mock:genDb mock:server"
},
npm run mock
を実行して、
http://localhost:3001/sample
にアクセス。
axios
install
$ npm install axios
準備
axios共通処理を記述。
src/state/apiClient.ts
import axios from 'axios';
export const client = axios.create({
baseURL: 'http://localhost:3001',
headers: {
'Content-Type': 'application/json',
},
timeout: 2000,
});
genDb.jsを修正してloginUser
を追加。
これを取得、表示する。
genDb.js
const db = {
// loginUserを追加
loginUser: {
id: 1,
name: 'Login User',
},
sample: [],
};
for (let i = 0; i < 5; i++) {
db.sample.push({
id: i,
name: 'hoge' + i,
});
}
console.log(JSON.stringify(db));
atomを定義。
src/state/tmpLoginUserState.ts
import { atom, selector, useRecoilValue } from 'recoil';
import { client } from './apiClient';
type LoginUser = {
id: number;
name: string;
};
// memo: 試しにaxiosでget処理かく
const tmpLoginUserState = atom<LoginUser>({
key: 'tmpLoginUserState',
// default値をselectorを使って設定しているが、
// defaultはundefined等にしておいてapi処理は別の箇所が良い気がする。
default: selector({
key: 'savedLoginuser',
get: async () => {
try {
const respons = await client.get('/loginUser');
return respons.data;
} catch (e) {
// memo: 仮error処理
console.log(e);
}
},
}),
});
export const useLoginUserState = (): LoginUser => {
return useRecoilValue(tmpLoginUserState);
};
Recoilと非同期処理の組み合わせは、また今度やってみる。
確認
axios呼び出しComponetをとりあえずSuspense
で囲む。
Suspense
の説明は省略。
Home.tsx
const Home: React.VFC = () => {
...
return (
<AppContainer>
{/* memo: Suspense仮置き */}
<Suspense fallback={<div>Loading...</div>}>
<CRAHeader
onApptitleClick={handleApptitleClick}
onDashboardButtonClick={handleDashboardButtonClick}
onDataListButtonClick={handleDataListButtonClick}
/>
</Suspense>
CRAHeader.tsx
const CRAHeader: React.VFC<CRAHeaderProps> = (props) => {
// axios使って値を設定しているstateの取得
const loginUser = useLoginUserState();
return (
<AppHeader
appTitle="CRA System"
onApptitleClick={props.onApptitleClick}
// 値を表示(AppHeaderに値を渡す)
userName={loginUser.name}
leftItems={[
{ id: 0, node: <DashboardButton onDashboardButtonClick={props.onDashboardButtonClick} /> },
{
id: 1,
node: <DataListButton onDataListButtonClick={props.onDataListButtonClick} />,
},
]}
/>
);
};
genDb.js
に記述したname
が表示されている。
続き
...準備中
前
Discussion