ReactJS のサービス層アプローチ
15220 ワード
履歴書
この投稿では、REST、GraphQL、またはフロントエンドとバックエンド (バックエンド、ストレージ、ローカル ファイルなど) との通信に使用するものすべての実装を分離するために実装しようとしている手法を紹介したいと思います.
動機
やあ.私はウェブ開発者です. PHP、Java、および .Net C# を使用した古い手法でアプリを構築した経験があります.だから私は、プログラミングの経験を苦痛にする醜いものやその他のものを見てきました: 保守が難しい、デバッグが難しい、スケーリングが難しい、テストが難しい (おそらく不可能)
私は数年前から ReactJS を使用してきましたが、あることに気付きました.ほとんどの開発者は、過去に犯したのと同じ過ちを犯しています (もちろん私も含まれます).
私が話しているのは、スパゲッティ コード、テスト不可能性、および実装の結合です.
ですから、物事を簡単にするために適用できるいくつかの原則があることを知っています (SOLID、DRY、KISS などについて話しています).それを改善したいと考えています.
サービス層アプローチ
わかりました、サービス接続を使用する反応コンポーネントを作成するとき、たとえば、この方法でそれを行う傾向があります
import axios from "axios";
import {useState, useEffect} from "react";
export function OrdersList() {
const [orders, setOrders] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
axios.get("/orders")
.then(({data}) => setOrders(data))
.catch(e => console.error(e))
.finally(() => setLoading(false));
}, []);
return (
<ul>
{orders.map(order => (
<li key={order.id}>{order.id}</li>
))}
</ul>
);
}
いいですね.しかし、同じエンドポイントを実装するコンポーネントが他にもある場合はどうでしょうか?エンドポイントが変わったら?各コンポーネントで更新する必要があります.また、マッピングや正規化などの処理をデータに追加する必要がある場合は、さらにコードを追加します.最後に、単体テストを追加する場合は、おそらく axios モック戦略を使用します.
私の提案は、必要に応じて引数を受け取り、必要なデータを返す関数のコレクション (リポジトリに近い) にデータ フェッチをカプセル化することです.
async function getAll() {
const result = await axios.get("/orders");
return result.data || [];
}
export const ordersService = {
getAll
};
これで、依存性注入を使用してこのように使用できます.
import {useState, useEffect} from "react";
// the ordersService is injected (dependencies injection)
export function OrdersList({ ordersService }) {
const [orders, setOrders] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
ordersService.getAll()
.then(orders => setOrders(orders))
.catch(e => console.error(e))
.finally(() => setLoading(false));
}, []);
return (
<ul>
{orders.map(order => (
<li key={order.id}>{order.id}</li>
))}
</ul>
);
}
その依存性注入を使用すると、使用するサービスの種類 (rest、graphql など) に関係なく、簡単にモックを作成できます.これは、「注文サービス」だけが背後にある魔法を知っているためです.
it ("Loads two orders") {
const mockOrdersService = {
getAll: async () => ([{ id: "mock-01" }, { id: "mock-02" }])
}
const { getByTestId } = render(<OrdersList ordersService={mockOrdersService} />);
...
}
今はとてもシンプルに思えます.満足しています.しかし、私はあなたにもっと多くの利点を見てもらいたい.
新しい注文を作成する必要があると考えてください.ポスト リクエストを使用し、バックエンドには特定のペイロードが必要です.
{
order: {
notes: "Extra cheese",
items: [{ sku: "hamburger-01" }]
},
customer: {
customer_id: "01",
registered_customer: true,
not_registered_customer_name: null
}
}
この場合、次の方法でサービス層に新しい関数を追加できます.
async function sendOrder({
notes,
items,
client_id,
not_registered_customer_name = null
}) {
const data = {
order: {
notes,
items
},
customer: {
customer_id,
not_registered_customer_name,
registered_customer: !!customer_id
}
};
const result = await axios.post("/orders", data);
return result.data || null;
}
export const ordersService = {
getAll,
sendOrder
}
注文を作成する必要がある場合は、必要な引数を渡すだけで、関数がデータをフォーマットします
ordersService.sendOrder({
client_id: "01",
notes: "Extra cheese",
items: [{ sku: "hamburger-01" }]
});
このアプローチにより、実装の詳細を分離し、コードの繰り返しを回避して、テストを容易にします.そして、コードの問題を分離するだけです.
次に、react Hooks と、graphql
useQuery
フックのように機能するように設計したフックを使用して、UI からロジックを分離することについて話したいと思います (私はそのフックが大好きですが、コードを維持するのが難しくなります)...しかしより良い提案をするために、フィードバックを待ったほうがよいと思います.また、私の英語に関する文章についてフィードバックをいただけますか.私はそれを大いに感謝します.平和! ✌️
Reference
この問題について(ReactJS のサービス層アプローチ), 我々は、より多くの情報をここで見つけました https://dev.to/chema/services-layer-approach-in-reactjs-1eo2テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol