برنامه نویسی

React Query Factory: قلاب های ایمن قابل استفاده مجدد با Axios & FSD

در دنیای مدرن توسعه Frontend ، بهینه سازی داده ها کلید ایجاد برنامه های پاسخگو و مقیاس پذیر است. استفاده از کتابخانه برای درخواست های ناهمزمان ، مانند @tanstack/react-query وت axios، یک سیستم مناسب برای کار با آن فراهم می کند REST APIsبا tRPC، یا GraphQLبشر

در هنگام تهیه برنامه ها ، ما اغلب باید اقدامات مشابهی را برای موجودات مختلف API انجام دهیم: لیستی از موارد را دریافت کنید ، یک مورد واحد را با شناسه بازیابی کنید ، یک مورد جدید ایجاد کنید و همچنین به روزرسانی ها و حذف ها را انجام دهید. این عملیات نامیده می شود CRUD (ایجاد ، خواندن ، به روزرسانی ، حذف).

من نحوه اجرای قلاب های جهانی Crud را با استفاده از کتابخانه های فوق الذکر به سبک کاربردی بر اساس الگوی کارخانه نشان خواهم داد. به جای کپی کردن کد برای هر نهاد ، می توانیم قلاب هایی تولید کنیم که به طور قابل توجهی میزان کد کپی چسبانده را کاهش می دهد و آن را انعطاف پذیر و قابل استفاده مجدد می کند. این رویکرد به راحتی مقیاس پذیر ، جهانی و برای هر API قابل اجرا است.

می توانید این مقاله را در وبلاگ من بخوانید یا در اینجا بخوانید.


@tanstack/react-query و axios

  • @tanstack/react-query -که قبلاً با عنوان React-Query شناخته می شد ، کتابخانه ای برای مدیریت وضعیت درخواست ها ، ذخیره کردن داده ها و همگام سازی آن با سرور است. این عملکرد را به طور قابل توجهی بهبود می بخشد و کار با درخواست های ناهمزمان را با انتزاع منطق API به دور از منطق ارائه داده ها در مؤلفه های React ساده می کند.
  • axios یک کتابخانه محبوب برای درخواست HTTP است. این امر به دلیل انعطاف پذیری و سهولت استفاده از جمله هنگام کار با عملیات ناهمزمان شناخته شده است.

هدف ما ایجاد تابعی است که به طور خودکار قلاب های Crud را برای هر نهاد ایجاد می کند و ما را از نوشتن همان کد بارها و بارها نجات می دهد. به عنوان مثال ، اگر ما نیاز به واکشی کاربران ، پست ها یا نظرات داریم ، نمی خواهیم همان کد دیگ بخار را برای هر نهاد تکرار کنیم.

اجرای قلاب های Crud به سبک عملکردی

بیایید با اجرای آن ادامه دهیم. ما یک تابع ایجاد خواهیم کرد createCrudHooks این مجموعه ای از قلاب های Crud را برای هر موجودیت خاص برمی گرداند.

مرحله 1: کتابخانه های لازم را وصل کنید

برای رسیدگی به پرس و جو و حافظه پنهان ، نصب کنید @tanstack/react-query وت axios (بعد از جابجایی به فهرست پروژه خود):

cd ./my-awesome-project
npm i axios react-query
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مرحله 2: عملکرد اصلی را ایجاد کنید

اکنون ایجاد کنید createCRUDHooks عملکرد ، که یک URL پایه و یک نام موجودیت (به عنوان مثال کاربران یا پست ها) می گیرد. این تابع مجموعه ای از قلاب ها را برای انجام عملیات CRUD برمی گرداند.

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'

import axios, { AxiosResponse } from 'axios'

const createCrudHooks = <T>(baseUrl: string, entity: string) => {
  const api = axios.create({
    baseURL: baseUrl,
  })

  // Fetch All (GET)
  const useFetchAll = (queryKey: string) => {
    return useQuery<T[]>(queryKey, async () => {
      const response: AxiosResponse<T[]> = await api.get(`/${entity}`)
      return response.data
    })
  }

  // Fetch One by ID (GET)
  const useFetchOne = (queryKey: string, id: string | number) => {
    return useQuery<T>([queryKey, id], async () => {
      const response: AxiosResponse<T> = await api.get(`/${entity}/${id}`)
      return response.data
    })
  }

  // Create (POST)
  const useCreate = () => {
    const queryClient = useQueryClient()

    return useMutation(
      async (data: Partial<T>) => {
        const response: AxiosResponse<T> = await api.post(`/${entity}`, data)
        return response.data
      },
      {
        onSuccess: () => {
          queryClient.invalidateQueries(entity)
        },
      }
    )
  }

  // Update (PUT)
  const useUpdate = () => {
    const queryClient = useQueryClient()

    return useMutation(
      async ({ id, data }: { id: string | number; data: Partial<T> }) => {
        const response: AxiosResponse<T> = await api.put(
          `/${entity}/${id}`,
          data
        )
        return response.data
      },
      {
        onSuccess: () => {
          queryClient.invalidateQueries(entity)
        },
      }
    )
  }

  // Delete (DELETE)
  const useDelete = () => {
    const queryClient = useQueryClient()

    return useMutation(
      async (id: string | number) => {
        const response: AxiosResponse<void> = await api.delete(
          `/${entity}/${id}`
        )
        return response.data
      },
      {
        onSuccess: () => {
          queryClient.invalidateQueries(entity)
        },
      }
    )
  }

  return {
    useFetchAll,
    useFetchOne,
    useCreate,
    useUpdate,
    useDelete
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

می توانید اطلاعات بیشتر در مورد استفاده از نوع ایمن با @tanstack/react-query را در مقاله query React Retact-Safe بخوانید.

بیایید با جزئیات بیشتری بررسی کنیم که چه چیزی را قلاب می کند createCrudHooks عملکرد برمی گردد:

HOOK USEFETCHALL

این قلاب برای بازیابی لیستی از موارد برای یک موجود خاص (به عنوان مثال ، لیستی از کاربران) درخواست دریافت می کند و از این استفاده می کند useQuery قلاب از @tanstack/react-query کتابخانه برای ذخیره و ردیابی وضعیت درخواست. این مجموعه از مواردی را که می تواند در هر مؤلفه React برای ارائه یا دستکاری بیشتر استفاده شود ، برمی گرداند. این تابع را می توان برای تصویب پارامترهای پرس و جو اضافی ، به عنوان مثال ، برای فیلتر کردن نتایج گسترش داد.

Hook UseFetchone

این قلاب توسط شناسه خود (شناسه منحصر به فرد) یک مورد واحد را بازیابی می کند. همچنین استفاده می کند useQuery، اما یک استدلال دوم – شناسه مورد. این به شما امکان می دهد داده ها را برای یک مورد خاص موجودی دریافت کنید.

قلاب

این قلاب برای ایجاد موارد جدید استفاده می شود. این یک درخواست پست به API صادر می کند و پس از ایجاد موفقیت آمیز یک مورد ، تماس می گیرد invalidateQueries برای تازه کردن حافظه پنهان ، بارگیری مجدد داده ها در سایر مؤلفه هایی که از قلاب های ما استفاده می شود ، بارگیری مجدد می شود.

HookeUpdate

از درخواست Put برای به روزرسانی یک مورد استفاده می شود. قلاب یک شیء حاوی شناسه مورد و داده ها را برای به روزرسانی می گیرد. پس از بروزرسانی موفق ، حافظه نهان داده نیز تازه می شود.

قلاب

این قلاب با استفاده از یک درخواست حذف ، یک مورد را توسط شناسه خود حذف می کند و پس از حذف ، حافظه پنهان داده را به روز می کند.

مرحله 3: تست ها

بیایید تست ها را اضافه کنیم تا اطمینان حاصل شود که کد ما به درستی و بدون خطا کار می کند.

ابتدا محیط آزمایش را با کتابخانه های زیر تنظیم کنید:

  • @testing-library/react – برای آزمایش اجزای React.
  • @testing-library/jest-dom – تطابق اضافی را برای ادعاها فراهم می کند (به عنوان مثال ، toBeInTheDocument).
  • vitest – یک دونده آزمون و چارچوبی برای نوشتن تست.
  • axios-mock-adapter – برای تمسخر درخواست ها از طریق Axios.
npm i @testing-library/react @testing-library/jest-dom vitest axios-mock-adapter -D
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برای اجرای تست ها ، اسکریپت زیر را به آن اضافه کنید package.json:

"scripts": {
  "test": "vitest"
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اکنون می توانید تست ها را اجرا کنید:

npm run test
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برای مثال بیایید یک فایل آزمایش ایجاد کنیم crudHooks.test.tsx:

import { QueryClient, QueryClientProvider } from 'react-query'
import { act, renderHook } from '@testing-library/react'
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
// Your hooks
import { createCrudHooks } from './crudHooks'

const mock = new MockAdapter(axios)

const createTestQueryClient = () => {
  return new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  })
}

const wrapper = ({ children }: { children: React.ReactNode }) => {
  const queryClient = createTestQueryClient()
  return (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  )
}

interface User {
  id: number
  name: string
  email: string
}

// Create hooks for user entity
const { useFetchAll, useFetchOne, useCreate, useUpdate, useDelete } =
  createCrudHooks<User>('https://api.example.io', 'users')

beforeEach(() => {
  mock.reset()
})

describe('CRUD Hooks', () => {
  it('should fetch all users successfully', async () => {
    const mockData = [
      { id: 1, name: 'Ivan Green', email: 'ivan@example.io' },
      { id: 2, name: 'Inna Green', email: 'inna@example.io' },
    ]

    mock.onGet('/users').reply(200, mockData)

    const { result, waitFor } = renderHook(() => useFetchAll('users'), {
      wrapper,
    })

    await waitFor(() => result.current.isSuccess)

    expect(result.current.data).toEqual(mockData)
  })

  it('should fetch one user by ID', async () => {
    const mockUser = { id: 1, name: 'Ivan Green', email: 'ivan@example.io' }

    mock.onGet('/users/1').reply(200, mockUser)

    const { result, waitFor } = renderHook(() => useFetchOne('users', 1), {
      wrapper,
    })

    await waitFor(() => result.current.isSuccess)

    expect(result.current.data).toEqual(mockUser)
  })

  it('should create a new user', async () => {
    const newUser = { id: 3, name: 'Sam Smith', email: 'sam@example.io' }

    mock.onPost('/users').reply(201, newUser)

    const { result, waitFor } = renderHook(() => useCreate(), { wrapper })

    act(() => {
      result.current.mutate({ name: 'Sam Smith', email: 'sam@example.io' })
    })

    await waitFor(() => result.current.isSuccess)

    expect(result.current.data).toEqual(newUser)
  })

  it('should update a user', async () => {
    const updatedUser = {
      id: 1,
      name: 'Ivan Updated',
      email: 'ivan.updated@example.io',
    }

    mock.onPut('/users/1').reply(200, updatedUser)

    const { result, waitFor } = renderHook(() => useUpdate(), { wrapper })

    act(() => {
      result.current.mutate({
        id: 1,
        data: { name: 'Ivan Updated', email: 'Ivan.updated@example.io' },
      })
    })

    await waitFor(() => result.current.isSuccess)

    expect(result.current.data).toEqual(updatedUser)
  })

  it('should delete a user', async () => {
    mock.onDelete('/users/1').reply(200)

    const { result, waitFor } = renderHook(() => useDelete(), { wrapper })

    act(() => {
      result.current.mutate(1)
    })

    await waitFor(() => result.current.isSuccess)

    expect(result.current.isSuccess).toBe(true)
  })
})
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما با استفاده از مجموعه قلاب Crud خود تست هایی ایجاد کرده ایم react-testing-libraryبا axios-mock-adapterوت vitestبشر این باعث می شود کد ما قابل اعتماد و به خوبی آزمایش شده باشد ، که برای توسعه برنامه های مقیاس پذیر و قابل نگهداری مهم است. آزمایش عملیات ناهمزمان به لطف درخواست تمسخر و ابزاری برای کار با قلاب بسیار ساده تر می شود.

استفاده از مثال در یک مؤلفه React

اکنون که قلاب های جهانی را ایجاد کرده ایم ، بیایید ببینیم چگونه از آنها در یک مؤلفه واقعی استفاده کنیم:

interface User {
  id: number
  name: string
  email: string
}

// Can be moved to a separate file for reuse
const { useFetchAll, useCreate, useUpdate, useDelete } = createCrudHooks<User>(
  'https://api.example.io',
  'users'
)

type UserProps = {
  user: User
}

const UserItem = ({ user }: UserProps) => {
  const { mutate: updateUser } = useUpdate()
  const { mutate: deleteUser } = useDelete()

  const handleUpdate = () => {
    updateUser({ id: user.id, data: { name: 'Ivan' } })
  }

  const handleDelete = () => {
    deleteUser(user.id)
  }

  return (
    <li key={user.id}>
      {user.name} - {user.email}
      <button onClick={handleUpdate}>Update namebutton>
      <button onClick={handleDelete}>Deletebutton>
    li>
  )
}

export const Users = () => {
  const { data: users, isLoading } = useFetchAll('users')
  const { mutate: createUser } = useCreate()

  const handleCreateUser = () => {
    createUser({ name: 'Darya', email: 'darya@example.io' })
  }

  if (isLoading) return <div>Loading users...div>

  return (
    <div>
      <h1>Usersh1>
      <ul>
        {users?.map((user: User) => (
          <UserItem key={user.id} user={user} />
        ))}
      ul>
      <button onClick={handleCreateUser}>Create userbutton>
    div>
  )
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این رویکرد چه مشکلی دارد؟

هنگام استفاده از بسته بندی ها برای قلاب ، می توانید با شماره های تایپ همانطور که در این بحث GitHub توضیح داده شده است ، روبرو شوید. یکی از گزینه ها استفاده از کارخانه های پرس و جو است که در ادامه در نظر خواهیم گرفت. می توانید کارخانه خود را بنویسید یا از کارخانه کلید Query Query استفاده کنید.

نمونه ای از استفاده از یک کارخانه پرس و جو در یک برنامه ساخته شده بر روی اصول طراحی دارای ویژگی (FSD)

به عنوان یک مقیاس پروژه ، اطمینان از مدولار بودن و سازمان آن مهم می شود. یکی از رویکردهای معماری ، طراحی دارای ویژگی (FSD) است که به سازماندهی یک برنامه با عملکرد کمک می کند و لایه های مسئولیت را ترسیم می کند. بیایید نحوه استفاده FSD را در پروژه ای که استفاده می کند در نظر بگیریم @tanstack/react-queryبشر

طراحی دارای ویژگی چیست؟

FSD (طراحی با ویژگی) یک رویکرد معماری است که در آن یک برنامه بر اساس عملکرد به ماژول های مستقل (ویژگی ها) تقسیم می شود. هدف اصلی بهبود مقیاس پذیری ، حفظ و خوانایی پروژه است. FSD به جلوگیری از رشد کنترل نشده پایگاه کد کمک می کند و آزمایش را ساده می کند.

سطح FSD

  • برنامه: تنظیمات جهانی ، مسیریابی و ارائه دهندگان.
  • صفحات: صفحات کامل یا قسمتهای بزرگ یک صفحه برای مسیریابی تو در تو.
  • ویراچه: قطعات بزرگ خودکشی از عملکرد یا رابط ، معمولاً اجرای یک سناریوی کل کاربر.
  • ویژگی: اجرای قابل استفاده مجدد از ویژگی های کامل محصول ، یعنی اقداماتی که ارزش کسب و کار را به کاربر ارائه می دهد.
  • موجودات: اشیاء منطقی منطقی تجارت ، مانند کاربران ، سفارشات و محصولات.
  • مشترک: ماژول های مشترک ، مانند مؤلفه های UI ، برنامه های کاربردی و یاران API.

کارخانه پرس و جو و FSD

حال بیایید با استفاده از معماری پروژه نگاه کنیم query factory و FSD رویکرد

ساختار پروژه:

src/
├── app/                   // Global configurations, routing, and providers
├── entities/              // Entities
│   └── pokemon/
│       ├── api/
│       ├── model/
│       └── ui/
├── features/              // Features
├── widgets/               // Widgets
├── pages/                 // Pages (PokemonsPage for example)
└── shared/                // Common modules, such as UI components, utilities..
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مشترک (منابع مشترک)

مشترک شامل مؤلفه های مشترک ، برنامه های کاربردی ، تنظیمات و یاران API عمومی است. بیایید بنویسیم createQueries عملکرد ، که به عنوان یک کارخانه پرس و جو خدمت می کند. بیایید فرض کنیم که ما از یک API ساده Crud برای برقراری ارتباط با سرور استفاده می کنیم:

import { keepPreviousData, queryOptions } from '@tanstack/react-query';
import * as qs from 'qs';
import { createQueryFn } from '../../api/createQueryFn';
import { createMutationFn } from '../../api/createMutationFn';
import { createDeleteMutationFn } from '../../api/createDeleteMutationFn';
import { createUpdateMutationFn } from '../../api/createUpdateMutationFn';

// We pass generics so that when we write code later, the types are displayed correctly.
export const createQueries = <
  CreateResponse,
  CreateBody,
  ReadResponse,
  ReadOneResponse,
  UpdateResponse,
  UpdateBody,
  DeleteResponse,
  DeleteParams
>(
  entity: string
) => ({
  all: () =>
    queryOptions({
      queryKey: [entity],
    }),
  create: () => ({
    mutationKey: [entity],
    mutationFn: (body: CreateBody) =>
      createMutationFn<CreateResponse, CreateBody>({
        path: `/${entity}`,
        body,
      }),
    placeholderData: keepPreviousData,
  }),
  read: (filters) =>
    queryOptions({
      queryKey: [entity, filters],
      queryFn: () =>
        createQueryFn<ReadResponse>({
          path: `/${entity}?${qs.stringify(filters)}`,
        }),
      placeholderData: keepPreviousData,
    }),
  readOne: ({ id }) =>
    queryOptions({
      queryKey: [entity, id],
      queryFn: () =>
        createQueryFn<ReadOneResponse>({
          path: `/${entity}/${id}`,
        }),
      placeholderData: keepPreviousData,
    }),
  update: () => ({
    mutationKey: [entity],
    mutationFn: ({ id, body }) =>
      createUpdateMutationFn<UpdateResponse, UpdateBody>({
        path: `/${entity}/${id}`,
        body,
      }),
    placeholderData: keepPreviousData,
  }),
  delete: () => ({
    mutationKey: [entity],
    mutationFn: (params: DeleteParams) =>
      createDeleteMutationFn<DeleteResponse>({
        path: `/${entity}/${params.id}`,
      }),
    placeholderData: keepPreviousData,
  }),
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این عملکرد یک شی را با تنظیمات پرس و جو برای عملیات اساسی CRUD برمی گرداند.

بیایید یک فایل آزمایش ایجاد کنیم createQueries.test.ts:

import { describe, it, expect, vi } from 'vitest';
import { createQueries } from './createQueries.ts';
import * as api from '../../api/createQueryFn';
import * as mutationApi from '../../api/createMutationFn';

vi.mock('../../api/createQueryFn', () => ({
  createQueryFn: vi.fn(),
}));

vi.mock('../../api/createMutationFn', () => ({
  createMutationFn: vi.fn(),
}));

vi.mock('../../api/createDeleteMutationFn', () => ({
  createDeleteMutationFn: vi.fn(),
}));

vi.mock('../../api/createUpdateMutationFn', () => ({
  createUpdateMutationFn: vi.fn(),
}));

describe('createQueries', () => {
  const entity = 'user';

  it('should return correct query options for "all"', () => {
    const queries = createQueries(entity);
    const result = queries.all();

    expect(result.queryKey).toEqual([entity]);
  });

  it('should create mutation for "create"', () => {
    const queries = createQueries(entity);
    const body = { name: 'Alexander' };

    queries.create().mutationFn(body);

    expect(mutationApi.createMutationFn).toHaveBeenCalledWith({
      path: `/${entity}`,
      body,
    });
  });

  it('should return correct query options for "read"', () => {
    const queries = createQueries(entity);
    const filters = { page: 1 };

    queries.read(filters).queryFn();

    expect(api.createQueryFn).toHaveBeenCalledWith({
      path: `/${entity}?page=1`,
    });
  });

  it('should return correct query options for "readOne"', () => {
    const queries = createQueries(entity);
    const id = 123;

    queries.readOne({ id }).queryFn();

    expect(api.createQueryFn).toHaveBeenCalledWith({
      path: `/${entity}/${id}`,
    });
  });

  it('should create mutation for "update"', () => {
    const queries = createQueries(entity);
    const id = 123;
    const body = { name: 'Updated' };

    queries.update().mutationFn({ id, body });

    expect(mutationApi.createUpdateMutationFn).toHaveBeenCalledWith({
      path: `/${entity}/${id}`,
      body,
    });
  });

  it('should create mutation for "delete"', () => {
    const queries = createQueries(entity);
    const params = { id: 123 };

    queries.delete().mutationFn(params);

    expect(mutationApi.createDeleteMutationFn).toHaveBeenCalledWith({
      path: `/${entity}/${params.id}`,
    });
  });
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اکنون عملکرد ما آزمایش شده است

موجودات

نهادها اشیاء اصلی دامنه هستند که برنامه با آن کار می کند ، به عنوان مثال ، User یا Pokemonبشر هر موجودیت ماژول خاص خود را دارد که شامل تعاریف نوع داده ، قلاب های Crud ، اجزای UI برای نمایش و احتمالاً آداپتورها برای تعامل با API است.

ساختار مثال Pokemon موجودیت:

src/
└── entities/
    └── pokemon/
        ├── api/
        │   ├── pokemon.query.ts      // Pokemon Queries
        │   ├── usePokemon.tsx        // Hook for getting one Pokemon
        │   ├── usePokemonCreate.tsx  // Hook for Pokemon creation
        │   ├── usePokemonDelete.tsx  // Hook for Pokemon deletion
        │   ├── usePokemonList.tsx    // Hook for getting Pokemon list
        │   └── usePokemonUpdate.tsx  // Hook for Pokemon update
        └── model/
            └── Pokemon.ts            // Types for Pokemon entity
        └── ui/
            ├── PokemonList.tsx       // React component for Pokemon list
            └── PokemonCard.tsx       // React component for Pokemon card
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • API/UsePokemon*.ts: اجرای قلاب Crud برای API Pokemon.
  • model/pokemon.ts: تعاریف نوع داده برای Pokemon موجودیت
  • ui/pokemonlist.tsx وت ui/pokemoncard.tsx: مؤلفه های UI برای موجودیت Pokemon (نمایش لیستی از Pokemon و یک Pokemon جداگانه).

نمونه pokemon.query.ts با استفاده از createQueries:

import { createQueries } from '../../../shared/lib/createQueries/createQueries';
import {
  CreatePokemonResponse,
  CreatePokemonBody,
  ReadPokemonResponse,
  ReadOnePokemonResponse,
  UpdatePokemonResponse,
  UpdatePokemonBody,
  DeletePokemonResponse,
  DeletePokemonParams,
} from '../model/Pokemon';

export const pokemonQueries = createQueries<
  CreatePokemonResponse,
  CreatePokemonBody,
  ReadPokemonResponse,
  ReadOnePokemonResponse,
  UpdatePokemonResponse,
  UpdatePokemonBody,
  DeletePokemonResponse,
  DeletePokemonParams
>('pokemon');
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در createQueries، انواع مورد نیاز برای تایپ مناسب و نام نهاد را با توجه به قرارداد پس زمینه منتقل می کنیم. سپس استفاده می کنیم pokemonQueries برای ایجاد لازم CRUD قلاب ، همانطور که با usePokemonList:

import { useQuery } from '@tanstack/react-query';
import { pokemonQueries } from './pokemon.query';

type Props = {
  limit: number;
  offset: number;
};

export const usePokemonList = ({ limit, offset }: Props) => {
  return useQuery({
    ...pokemonQueries.read({ limit, offset }),
  });
};
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما همه را بدست می آوریم CRUD قلاب های مورد نیاز برای برنامه ، که می تواند در اجزای UI یا سایر لایه های برنامه استفاده شود. این باعث می شود ساختار کد بیشتر محصور و سازگار شود FSDبشر

بسته به توافق های موجود در پروژه خود ، می توانید یک کارخانه برای قلاب های Crud بنویسید ، یا فقط به صورت دستی فقط موارد مورد نیاز خود را ایجاد کنید ، همانطور که ما انجام دادیم.

ویژگی

ویژگی ها نشان دهنده اقدامات خاص کاربر است که ارزش کسب و کار را ارائه می دهد. به عنوان مثال ، ویرایش یا ایجاد یک Pokemon جدید. این ویژگی ها می توانند از موجودات استفاده کنند و در صورت لزوم آنها را در قوانین تجاری اضافی بپیچانند.

ساختار مثال create-pokemon ویژگی:

src/
└── features/
    └── create-pokemon/
        ├── model/
        └── ui/
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • مدل/: ممکن است حاوی قلاب یا عملکردی برای ایجاد Pokemon باشد (به عنوان مثال ، استفاده usePokemonCreate از entities/pokemon/api).
  • UI/: مؤلفه هایی که فرم Add Pokemon یا UI را برای تأیید علاوه بر این ارائه می دهند.
import { usePokemonCreate } from
'../../../entities/pokemon/api/usePokemonCreate';
import { CreatePokemonBody } from '../../../entities/pokemon/model/Pokemon';

type Props = {
  pokemon: CreatePokemonBody;
};

export const CreatePokemon = ({ pokemon }: Props) => {
  const { mutate: createPokemon } = usePokemonCreate();

  const handleCreate = () => {
    createPokemon({
      name: pokemon.name,
    });
  };

  return (
    <button
      onClick={handleCreate}
      className="bg-green-500 text-white p-2 rounded hover:bg-green-600 transition-colors"
    >
      Create {pokemon.name}
    </button>
  );
};
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

با استفاده از مؤلفه:

<CreatePokemon pokemon={{ name: 'Pikachu' }} />
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ویراچه

ویجت ها ماژول های UI بزرگتر هستند که می توانند از چندین ویژگی و موجودات تشکیل شوند. به عنوان مثال ، الف PokemonProfile ویجت ممکن است شامل لیستی از تمام سریال ها و بازی هایی باشد که Pokemon در آن ظاهر شده است ، اطلاعات مربوط به مشخصات آن و غیره.

می توانید نمونه مفصلی از این رویکرد را در پروژه نسخه ی نمایشی پیدا کنید.

نتیجه گیری

ما یک انعطاف پذیر و قابل استفاده مجدد را اجرا کرده ایم CRUD سیستم قلاب با استفاده از @tanstack/react-query وت axiosبشر اکنون می توانید به راحتی از این قلاب ها برای هر موجود در API خود استفاده کنید ، کد خود را به طور قابل توجهی ساده کرده و خوانایی را بهبود بخشید.

این روش به مقیاس بندی برنامه به راحتی و به حداقل رساندن کپی کردن کد کمک می کند. متقاضی FSD اصول به ساختار پروژه کمک می کند و آن را مقیاس پذیر تر و حفظ می کند. ترکیب این رویکردها به شما امکان می دهد تا برنامه های انعطاف پذیر ایجاد کنید که در آن مدولار بودن و استقلال مؤلفه ، پروژه را برای پشتیبانی و گسترش آسان می کند.

سعی کنید این رویکرد را در پروژه خود بگنجانید ، و به سرعت متوجه خواهید شد که چگونه مدیریت وضعیت درخواست را ساده می کند و آن را در مؤلفه های React ادغام می کند.

پیوندهای جالب:

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا