React đến tRPC
AWS Plugin cho Nx cung cấp một generator để nhanh chóng tích hợp tRPC API của bạn với một website React. Nó thiết lập tất cả cấu hình cần thiết để kết nối đến các backend tRPC của bạn, bao gồm hỗ trợ xác thực AWS IAM và Cognito cũng như xử lý lỗi phù hợp. Tích hợp này cung cấp type safety đầy đủ từ đầu đến cuối giữa frontend và (các) backend tRPC của bạn.
Điều kiện tiên quyết
Phần tiêu đề “Điều kiện tiên quyết”Trước khi sử dụng generator này, đảm bảo ứng dụng React của bạn có:
- Một file
main.tsxrender ứng dụng của bạn - Một phần tử JSX
<App/>nơi mà tRPC provider sẽ được tự động inject vào - Một tRPC API đang hoạt động (được tạo bằng tRPC API generator)
- Cognito Auth được thêm thông qua
ts#react-website-authgenerator nếu kết nối một API sử dụng Cognito hoặc IAM auth
Ví dụ về cấu trúc main.tsx bắt buộc
import { StrictMode } from 'react';import * as ReactDOM from 'react-dom/client';import App from './app/app';
const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement,);root.render( <StrictMode> <App /> </StrictMode>,);Cách sử dụng
Phần tiêu đề “Cách sử dụng”Chạy Generator
Phần tiêu đề “Chạy Generator”- Install the Nx Console VSCode Plugin if you haven't already
- Open the Nx Console in VSCode
- Click
Generate (UI)in the "Common Nx Commands" section - Search for
@aws/nx-plugin - api-connection - Fill in the required parameters
- Click
Generate
pnpm nx g @aws/nx-plugin:api-connectionyarn nx g @aws/nx-plugin:api-connectionnpx nx g @aws/nx-plugin:api-connectionbunx nx g @aws/nx-plugin:api-connectionYou can also perform a dry-run to see what files would be changed
pnpm nx g @aws/nx-plugin:api-connection --dry-runyarn nx g @aws/nx-plugin:api-connection --dry-runnpx nx g @aws/nx-plugin:api-connection --dry-runbunx nx g @aws/nx-plugin:api-connection --dry-runTùy chọn
Phần tiêu đề “Tùy chọn”| Parameter | Type | Default | Description |
|---|---|---|---|
| sourceProject Required | string | - | The source project which will call the API |
| targetProject Required | string | - | The target project containing your API |
Kết quả của Generator
Phần tiêu đề “Kết quả của Generator”Generator tạo ra cấu trúc sau trong ứng dụng React của bạn:
Thư mụcsrc
Thư mụccomponents
- <ApiName>ClientProvider.tsx Thiết lập các tRPC client và bindings đến (các) schema backend của bạn. ApiName sẽ được giải quyết thành tên của API
- QueryClientProvider.tsx TanStack React Query client provider
Thư mụchooks
- useSigV4.tsx Hook để ký các HTTP request với SigV4 (chỉ IAM)
- use<ApiName>.tsx Một hook cho backend API đã cho.
Ngoài ra, nó cài đặt các dependencies bắt buộc:
@trpc/client@trpc/tanstack-react-query@tanstack/react-queryaws4fetch(nếu sử dụng IAM auth)
Sử dụng Code được tạo ra
Phần tiêu đề “Sử dụng Code được tạo ra”Sử dụng tRPC Hook
Phần tiêu đề “Sử dụng tRPC Hook”Generator cung cấp một hook use<ApiName> cho bạn truy cập đến tRPC client có type-safe:
import { useQuery, useMutation } from '@tanstack/react-query';import { useMyApi } from './hooks/useMyApi';
function MyComponent() { const trpc = useMyApi();
// Ví dụ query const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
// Ví dụ mutation const mutation = useMutation(trpc.users.create.mutationOptions());
const handleCreate = () => { mutation.mutate({ name: 'John Doe', email: 'john@example.com', }); };
if (isLoading) return <div>Loading...</div>;
return ( <ul> {data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}Xử lý lỗi
Phần tiêu đề “Xử lý lỗi”Tích hợp này bao gồm xử lý lỗi tích hợp sẵn để xử lý đúng các lỗi tRPC:
function MyComponent() { const trpc = useMyApi();
const { data, error } = useQuery(trpc.users.list.queryOptions());
if (error) { return ( <div> <h2>Error occurred:</h2> <p>{error.message}</p> {error.data?.code && <p>Code: {error.data.code}</p>} </div> ); }
return ( <ul> {data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}Best Practices
Phần tiêu đề “Best Practices”Xử lý trạng thái Loading
Phần tiêu đề “Xử lý trạng thái Loading”Luôn xử lý các trạng thái loading và error để có trải nghiệm người dùng tốt hơn:
function UserList() { const trpc = useMyApi();
const users = useQuery(trpc.users.list.queryOptions());
if (users.isLoading) { return <LoadingSpinner />; }
if (users.error) { return <ErrorMessage error={users.error} />; }
return ( <ul> {users.data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> );}Optimistic Updates
Phần tiêu đề “Optimistic Updates”Sử dụng optimistic updates để có trải nghiệm người dùng tốt hơn:
import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
function UserList() { const trpc = useMyApi(); const users = useQuery(trpc.users.list.queryOptions()); const queryClient = useQueryClient();
const deleteMutation = useMutation( trpc.users.delete.mutationOptions({ onMutate: async (userId) => { // Hủy các fetches đang gửi đi await queryClient.cancelQueries(trpc.users.list.queryFilter());
// Lấy snapshot của dữ liệu hiện tại const previousUsers = queryClient.getQueryData( trpc.users.list.queryKey(), );
// Xóa user một cách optimistic queryClient.setQueryData(trpc.users.list.queryKey(), (old) => old?.filter((user) => user.id !== userId), );
return { previousUsers }; }, onError: (err, userId, context) => { // Khôi phục dữ liệu trước đó khi có lỗi queryClient.setQueryData( trpc.users.list.queryKey(), context?.previousUsers, ); }, }), );
return ( <ul> {users.map((user) => ( <li key={user.id}> {user.name} <button onClick={() => deleteMutation.mutate(user.id)}>Delete</button> </li> ))} </ul> );}Prefetching Data
Phần tiêu đề “Prefetching Data”Prefetch dữ liệu để có hiệu suất tốt hơn:
function UserList() { const trpc = useMyApi(); const users = useQuery(trpc.users.list.queryOptions()); const queryClient = useQueryClient();
// Prefetch chi tiết user khi hover const prefetchUser = async (userId: string) => { await queryClient.prefetchQuery(trpc.users.getById.queryOptions(userId)); };
return ( <ul> {users.map((user) => ( <li key={user.id} onMouseEnter={() => prefetchUser(user.id)}> <Link to={`/users/${user.id}`}>{user.name}</Link> </li> ))} </ul> );}Infinite Queries
Phần tiêu đề “Infinite Queries”Xử lý phân trang với infinite queries:
function UserList() { const trpc = useMyApi();
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery( trpc.users.list.infiniteQueryOptions( { limit: 10 }, { getNextPageParam: (lastPage) => lastPage.nextCursor, }, ), );
return ( <div> {data?.pages.map((page) => page.users.map((user) => <UserCard key={user.id} user={user} />), )}
{hasNextPage && ( <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}> {isFetchingNextPage ? 'Loading...' : 'Load More'} </button> )} </div> );}Điều quan trọng cần lưu ý là infinite queries chỉ có thể được sử dụng cho các procedure có thuộc tính input có tên là cursor.
Type Safety
Phần tiêu đề “Type Safety”Tích hợp này cung cấp type safety hoàn toàn từ đầu đến cuối. IDE của bạn sẽ cung cấp autocompletion đầy đủ và type checking cho tất cả các API call của bạn:
function UserForm() { const trpc = useMyApi();
// ✅ Input được typed đầy đủ const createUser = trpc.users.create.useMutation();
const handleSubmit = (data: CreateUserInput) => { // ✅ Type error nếu input không khớp với schema createUser.mutate(data); };
return <form onSubmit={handleSubmit}>{/* ... */}</form>;}Các type được tự động suy ra từ các định nghĩa router và schema của backend, đảm bảo rằng bất kỳ thay đổi nào đối với API của bạn đều được phản ánh ngay lập tức trong code frontend mà không cần build.
Thông tin thêm
Phần tiêu đề “Thông tin thêm”Để biết thêm thông tin, vui lòng tham khảo tài liệu tRPC TanStack React Query.
Bạn cũng có thể tham khảo trực tiếp tài liệu TanStack Query.