Kyle Chen's Blog

Action speaks louder than Words

0%

json-server を使った Mock サービスの作成方法

はじめに

Web アプリケーション開発では、バックエンド API がまだ完成していない場合に、Mock サービスを使うことで開発をスムーズに進めることができます。json-server は、JSON ファイルを使って簡単に Mock API を構築できる便利なツールです。このブログでは、json-server を使った Mock サービスの作成手順を説明します。


必要な準備

以下のツールがインストールされていることを確認してください:

  • Node.js
  • npm (Node.js に含まれています)

json-server のインストール

まず、json-server をグローバルにインストールします。

1
npm install -g json-server

データベースファイル (db.json) の準備

json-server は、JSON ファイルをデータベースとして使用します。以下のようなファイルを用意してください。

db.json の例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"users": [
{
"id": 1,
"name": "山田太郎",
"email": "taro@example.com"
},
{
"id": 2,
"name": "鈴木花子",
"email": "hanako@example.com"
}
],
"posts": [
{
"id": 1,
"title": "はじめてのブログ",
"content": "これはサンプルのブログ記事です。"
}
]
}

このファイルはアプリケーションのルートディレクトリに配置してください。


Mock サーバーの起動

以下のコマンドで json-server を起動します:

1
json-server --watch db.json

成功すると、以下のようなメッセージが表示されます:

1
2
3
4
5
6
7
8
9
10
11
\{^_^}/ hi!

Loading db.json
Done

Resources
http://localhost:3000/users
http://localhost:3000/posts

Home
http://localhost:3000

API の使用例

  1. ユーザーリストの取得

    GET リクエストを送信:

    1
    curl http://localhost:3000/users
  2. 新しいユーザーの追加

    POST リクエストを送信:

    1
    curl -X POST -H "Content-Type: application/json" -d '{"name": "佐藤一郎", "email": "ichiro@example.com"}' http://localhost:3000/users
  3. ユーザー情報の更新

    PUT リクエストを送信:

    1
    curl -X PUT -H "Content-Type: application/json" -d '{"name": "佐藤次郎", "email": "jiro@example.com"}' http://localhost:3000/users/1
  4. ユーザーの削除

    DELETE リクエストを送信:

    1
    curl -X DELETE http://localhost:3000/users/1

まとめ

json-server を使えば、簡単に Mock サービスを構築し、フロントエンド開発を効率化できます。設定もシンプルで、JSON ファイルを編集するだけで API を変更できるため、とても柔軟です。

ぜひ、開発プロジェクトで活用してみてください!

Redux の使い方

1. Redux の基本的な概念

Redux を使うためには、まず以下の基本的な概念を理解することが重要です:

  • Store(ストア):アプリケーションの状態が格納される場所。すべてのデータが一元管理されます。
  • Action(アクション):状態を変更したいときに発行するオブジェクトで、type プロパティと必要に応じて他のデータを含んでいます。
  • Reducer(リデューサー):アクションが発行されると、リデューサーが新しい状態を生成します。これは純粋な関数で、前の状態とアクションを受け取り、新しい状態を返します。
  • Dispatch(ディスパッチ):アクションをストアに送る関数。これを使って状態を更新します。

2. Redux のインストール

まず、React プロジェクトに Redux をインストールします:

1
npm install redux react-redux
  • redux は状態管理のためのライブラリです。
  • react-redux は React と Redux を接続するためのライブラリです。

3. Redux ストアの作成

まず、ストアを作成します。ストアは、アプリケーションの状態を管理する中心的な役割を果たします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// store.js
import { createStore } from 'redux';

// 初期状態
const initialState = {
count: 0
};

// リデューサー関数
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};

// ストアを作成
const store = createStore(counterReducer);

export default store;

4. Action の定義

アクションは、状態の更新を表すオブジェクトです。アクションには少なくとも type プロパティを含める必要があります。

1
2
3
4
5
6
7
8
// actions.js
export const increment = () => ({
type: 'INCREMENT'
});

export const decrement = () => ({
type: 'DECREMENT'
});

5. React コンポーネントで Redux ストアを使用する

React アプリケーションで Redux のストアを使うために、Provider コンポーネントでストアをラップし、アプリ全体にストアを提供します。

1
2
3
4
5
6
7
8
9
10
11
12
13
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';

ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);

6. Redux ステートをコンポーネントで接続

React コンポーネントで Redux のステートを使うためには、useSelector を使ってストアからデータを取得し、useDispatch を使ってアクションをディスパッチします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// App.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './actions';

const App = () => {
// Redux ストアから state を取得
const count = useSelector(state => state.count);

// アクションをディスパッチする関数
const dispatch = useDispatch();

return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
};

export default App;

7. Redux の使い方のまとめ

  • Store:状態を格納する場所。createStore を使って作成します。
  • Action:状態を変更するためにディスパッチされるオブジェクト。type プロパティを含みます。
  • Reducer:状態変更のためのロジック。アクションを受け取り、新しい状態を返します。
  • useSelector:React コンポーネント内で Redux の状態を取得するフック。
  • useDispatch:アクションをストアにディスパッチするフック。

これで、Redux を使

1. React における「ローカルストレージ」の問題

  • React のコンポーネント自体には state(状態)があり、これは各部屋にある小さなホワイトボードのようなものです。このホワイトボードはとても便利で、何かを書く(データを保存する)ことも、消す(データを更新する)ことも簡単です。

  • しかし、複数の部屋がこのホワイトボードの内容を共有する必要があると、問題が発生します:

  • 例えば、部屋 A に「ユーザーのログイン状態」が書かれている場合、部屋 B もユーザーがログインしているかどうかを知る必要があります。

  • この場合、A 部屋から B 部屋に情報を渡す必要があります(props を使って)。でも、部屋がさらに増えて、A -> B -> C -> D というように、情報を渡し続けるのは非常に面倒です。

2. Redux が提供する「集中ストレージ」

  • Redux の役割は、大きな倉庫を作り、すべての部屋(コンポーネント)が重要な情報をその倉庫に保存できるようにすることです。

  • 各部屋が情報を必要とするときは、倉庫から直接取り出すことができ、情報を一つ一つ渡す必要はありません。

  • 簡単な例を挙げてみましょう:

  • あなたがコンビニを経営しているとしましょう。

  • React の state の方法は、各レジ係(コンポーネント)が自分の現在の収支を記録することです。

  • しかし、コンビニ全体の総収入を集計する場合、すべてのレジ係の記録を持ってきて、1つ1つ加算しなければなりません。

  • Redux の方法では、すべてのレジ係(コンポーネント)が同じ帳簿(Redux 倉庫)に記帳します。そして、総収入を知りたいときは、帳簿を確認すれば良いのです。

3. Redux の核心機能

  • Redux はいくつかのシンプルで強力な機能を提供します:

  • 統一ストレージ(Store):中央の倉庫のようなもので、すべてのデータがここで一元管理されます。

  • アクション(Action):倉庫のデータを更新したい場合は、「アクション」を発行する必要があります。これは、何を変更するかを示す申請書のようなものです。

  • リデューサー(Reducer):すべての更新は事前に定められたルールに従って行われ、各変更が予測可能で管理可能であることを保証します。

4. Redux を使った実際のプロジェクトでの利点

  • 例えば、あなたが eコマースサイトを開発しているとしましょう。以下の機能があります:

  • ユーザーがログインした後、ショッピングカートにはユーザーのアイテム数が表示されます。

  • 他のページ(例えば、注文ページ)でも、ユーザーのログイン状態やカートの内容を知る必要があります。

  • ユーザーがカートページで商品を追加した場合、決済ページにもすぐにそのデータを更新しなければなりません。

  • もし Redux を使わない場合、次のようにする必要があります:

  • ショッピングカートページで state を更新します。

  • その後、props を使ってデータを結算ページに渡します。

  • しかし、Redux を使うと、次のように簡単になります:

  • ショッピングカートページで「商品を追加する」というアクション(Action)を送信します。

  • Redux はそのアクションに基づいて倉庫のデータを更新します(Reducer)。

  • 他のページ(例えば、決済ページ)は自動的に最新のデータを取得します。なぜなら、それらは直接倉庫に接続しているからです(connect を通じて)。

カスタムフック

紹介

  • Reactでは、カスタムフックを作成することで、複雑なロジックを再利用可能な関数として抽象化できます。
  • カスタムフックは、useStateuseEffectなどのReactのフックを組み合わせて、状態管理や副作用の処理を行うカスタムのフックを作成します。

使用方法

カスタムフックは通常、useで始まる名前を付ける必要があります。これにより、Reactがそれがフックであることを認識します。

例: カスタムフック useLocalStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { useState } from 'react';

// カスタムフックの定義
function useLocalStorage(key, initialValue) {
// ローカルストレージから値を取得
const saved = localStorage.getItem(key);
const initial = saved ? JSON.parse(saved) : initialValue;

const [value, setValue] = useState(initial);

// 状態が更新された時にローカルストレージも更新
const setStoredValue = (newValue) => {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
};

return [value, setStoredValue];
}

function Example() {
const [name, setName] = useLocalStorage('name', 'John Doe');

return (
<div>
<h1>こんにちは、{name}!</h1>
<button onClick={() => setName('Alice')}>名前を変更</button>
</div>
);
}

特徴

  • カスタムフックは再利用可能なロジックの集約であり、複数のコンポーネントで同じロジックを使いたい場合に便利です。
  • フックの内部でuseStateやuseEffectを使い、状態や副作用を管理できます。
  • カスタムフックはコンポーネントに依存せず、どのコンポーネントでも使えるため、コードの重複を減らすのに役立ちます。

    注意点

  • カスタムフックは通常、コンポーネントの外で定義し、コンポーネント内で使用します。
  • カスタムフックもReactのルールに従い、コンポーネント内のトップレベルで使用する必要があります。

useRefとは

紹介

  • useRef は React の Hook の一つで、関数コンポーネント内で参照(reference)を作成するために使用されます。
  • 通常、DOM要素にアクセスしたいときに使われますが、状態の管理にも使用することができます。useRef はコンポーネントの再レンダリングをトリガーしません。

使用方法

  • useRef を使うと、DOM要素に対して直接アクセスできる「ref」を作成することができます。
  • useRef はオブジェクトを返し、その current プロパティを通じて参照したい要素にアクセスできます。
import React, { useRef } from 'react';

function Example() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus the input</button>
    </div>
  );
}

### 特徴
- useRef の値は再レンダリングを引き起こしません。
- コンポーネントが再レンダリングされても、ref の値は保持されます。
- 状態としての管理には useState を使用し、useRef は主に DOM 操作やその他の参照を管理するために使用されます。

https://github.com/KyleAndKelly/ReactStepByStep/tree/main/react-basic

Reactのコンポーネント

紹介

  • コンポーネントはユーザーインターフェースの一部であり、独自のロジックと外観を持つことができます。コンポーネント同士は入れ子にしたり、複数回再利用したりすることができます。
  • コンポーネントベースの開発により、開発者は積み木を組み立てるように、大規模なアプリケーションを構築できます。
  • image.png

使用方法

  • Reactでは、コンポーネントとは頭文字が大文字の関数であり、その内部にコンポーネントのロジックとUI(ユーザーインターフェース)が含まれています。コンポーネントをレンダリングするには、タグとして記述するだけで済みます。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    function Son1(props) {
    console.log(props.name)
    console.log(props.age)
    console.log(props.isTrue)
    console.log(props.list)
    console.log(props.obj)
    console.log(props.funct)
    console.log(props.child)
    }

    function Son2(props) {
    console.log(props.children)
    }

    function Son3({onGetMsg}){
    const sonMsg = "hello bro"
    onGetMsg(sonMsg)
    }

    function Son4(props){
    console.log("now in Son4")
    console.log(props.msg)
    }

    function Son5(){
    return ( <Son6/>)
    }

    ReactのuseState

    紹介

  • useStateはReactのHook(関数)で、コンポーネントに状態変数を追加することを可能にします。これにより、コンポーネントのレンダリング結果を制御できます。

  • 通常のJavaScript変数とは異なり、状態変数が変更されるとコンポーネントのUIもそれに応じて更新されます(データ駆動のビュー)。
    image.png

    使用方法

  • useStateは関数であり、戻り値は配列です。

  • 配列の1つ目の要素は状態変数、2つ目の要素は状態変数を変更するためのset関数です。

  • useStateの引数はcountの初期値として使用されます。

    1
    const [count, setState] = React.useState(0)
  • Reactでは、状態は読み取り専用と見なされ、常に置き換える必要があります。直接状態を変更してもビューの更新は引き起こされません。

1
2
3
4
const updateCount = ()=>{
// count++ //not work
setState(count+2)
}

インターフェース説明

  • useEffectはReactのHook関数であり、Reactコンポーネント内でイベントによるものではなく、レンダリングそのものによって引き起こされる操作(副作用)を作成するために使用されます。たとえば、AJAXリクエストの送信やDOMの変更などが含まれます。

  • ページのレンダリング後、またはページ内のコンポーネントに変化があった際に実行される操作として理解できます。

  • 第1引数は関数で、副作用関数と呼ぶことができます。この関数内に実行したい操作を記述します。

  • 第2引数は配列(省略可能)で、この配列に依存項を指定します。異なる依存項が第1引数の関数の実行に影響を与えます。配列が空の場合、副作用関数はコンポーネントのレンダリング後に一度だけ実行されます。

使用方法

image.png

image.png

image.png

image.png

依存項

  • useEffectの副作用関数が実行されるタイミングは、指定された依存項によって異なります。
  • 依存項 副作用関数の実行タイミング
  • 依存項なし コンポーネント初期レンダリング+更新時
  • 空の配列 初期レンダリング時に1回のみ実行
  • 特定の依存項あり 初期レンダリング+依存項が変化した時

1. コメントリストのレンダリング

1.1. useState を使用してコメントリストを管理

useState を利用してコメントリストを管理します。

1.2. map メソッドを使ってリストデータを繰り返し処理し、レンダリング

map メソッドを使用して、リスト内のデータをレンダリングします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

const defaultList = [
{
rpid: 3,
user: {
uid: '13258165',
avatar: '',
uname: 'Jack',
},
content: 'jjjjjjj',
ctime: '10-18 08:15',
like: 88,
},
{
rpid: 2,
user: {
uid: '36080105',
avatar: '',
uname: 'Tom',
},
content: 'ttttttt',
ctime: '11-13 11:29',
like: 88,
},
{
rpid: 1,
user: {
uid: '30009257',
avatar,
uname: 'Kelly',
},
content: 'kkkkkkkkk',
ctime: '10-19 09:00',
like: 66,
},
]


function CommentItemList() {

return commentList.map(item=><div className="reply-item" key = {item.user.uid}>
{/* 头像 */}
<div className="root-reply-avatar">
<div className="bili-avatar">
<img
className="bili-avatar-img"
alt=""
/>
</div>
</div>

<div className="content-wrap">
{/* 用户名 */}
<div className="user-info">
<div className="user-name">{item.user.uname}</div>
</div>
{/* 评论内容 */}
<div className="root-reply">
<span className="reply-content">{item.content}</span>
<div className="reply-info">
{/* 评论时间 */}
<span className="reply-time">{item.ctime}</span>
{/* 评论数量 */}
<span className="reply-time">点赞数:{item.like}</span>
{item.user.uid !== user.uid && <span className="delete-btn" onClick={()=>onDeletClicked(item.user.uid)}>
删除
</span>}


</div>
</div>
</div>
</div>)
}



<div className="reply-list">
<CommentItemList/>
</div>


2. コメント削除機能

要件

  1. 自分のコメントにのみ削除ボタンを表示する。
  2. 削除ボタンをクリックすると、そのコメントを削除し、リストから非表示にする。

コアのアイデア

  1. 削除ボタンの表示 - 条件付きレンダリングを使用して、自分のコメントにだけ削除ボタンを表示します。
  2. 削除機能 - 現在のコメントの id を取得し、filter を使ってリストから該当するコメントを削除します。
1
2
3
4
5
6
7
8
9
10
11
12
13

function onDeletClicked(userId){
console.log(userId)
console.log(userId)
const newCommentList = commentList.filter(item=>item.user.uid!==userId)
setCommentList(newCommentList)

}

<span className="reply-time">点赞数:{item.like}</span>
{item.user.uid !== user.uid && <span className="delete-btn" onClick={()=>onDeletClicked(item.user.uid)}>
删除
</span>}

3. ナビゲーションタブのレンダリングとハイライトの実装

要件

クリックしたタブをハイライト表示します。

コアのアイデア

  1. クリックされたタブの type(一意の識別子)を記録します。
  2. タブをレンダリングする際、現在のタブの type と比較し、一致した場合にハイライト用のクラスを追加します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

const tabs = [
{ type: 'hot', text: '最热' },
{ type: 'time', text: '最新' },
]

const [navList, setNavList] = React.useState(tabs)

function onNavItemClick(type){
setType(type)
let sortedCommentList
if(type==='hot'){
sortedCommentList = commentList.slice().sort((a, b) => b.like - a.like)
}else{
sortedCommentList = commentList.slice().sort((a, b) => {
return new Date(b.ctime) - new Date(a.ctime);
});
}
setCommentList(sortedCommentList)

}




function NavListItemList() {
return navList.map(
item=>
<span
className={`nav-item ${currrentNavType === item.type&& 'active' }}` }
onClick={()=>onNavItemClick(item.type)}
key={item.type}>
{item.text}
</span>)
}

4. コメントリストの並び替え機能

要件

コメントリストに並び替え機能を追加します。
並び替え基準として「like 数」や「ctime」などを選択可能にします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function onNavItemClick(type){
setType(type)
let sortedCommentList
if(type==='hot'){
sortedCommentList = commentList.slice().sort((a, b) => b.like - a.like)
setCommentList()
}else{
sortedCommentList = commentList.slice().sort((a, b) => {
return new Date(b.ctime) - new Date(a.ctime);
});
}
setCommentList(sortedCommentList)

}