ããã«ã¡ã¯ãå»çãã©ãããã©ãŒã ç¬¬äžæ¬éšãããã¯ãéçºå®€æå±ãšã³ãžãã¢ã®é«æ©ã§ãã æ®æ®µã®æ¥åã§ã¯ã å»ç SaaS ãã©ãããã©ãŒã CLINICS ã®å»çæ©é¢åãã¢ããªã±ãŒã·ã§ã³ïŒä»¥äžãCLINICSïŒã®éçºãæ
åœããŠããŸãã CLINICS ã§ã¯ãæšå¹Ž 10 æé ãã React ã³ãŒãããŒã¹ã®ãªã¢ãŒããã¯ãã£ã«åãçµãã§ããŸãã ãã®åãçµã¿ã® 1 ã€ãšããŠãéåæç¶æ
管çã«é¢é£ããå®è£
ã Tanstack Query ã䜿ã£ãŠå·æ°ããŠããŸãã ãã®èšäºã§ã¯ãCLINICS ã«ããã Tanstack Query ã®æŽ»çšæ¹æ³ãå°å
¥èæ¯ãšçããå«ããŠç޹ä»ããŸãã ç®æ¬¡ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> Tanstack Query ã«ã€ã㊠Tanstack Query ãæŽ»çšããããã³ããšã³ãã¢ãŒããã¯ã㣠Resource Operation ã®å®è£
ãã£ã¬ã¯ããªæ§æ cache.ts queries.ts mutations.ts ViewModel ã®å®è£
ãã£ã¬ã¯ããªæ§æ ãµãŒãã¹ããŒã ããã³ããšã³ãã«éããã¹ããŒã ããã³ããšã³ãã«éããå Tanstack Query å°å
¥ã®èæ¯ãšã¢ãŒããã¯ãã£ã®çã èæ¯ïŒã³ãŒãå質ã®èª²é¡ çã 1ïŒTanstack Query ã®å°å
¥ã«ããéçºäœéšã®åäž çã 2ïŒã·ã³ãã«ãªã¬ã€ã€ãŒåå²ã«ããåŠç¿å®¹ææ§ã®åäž çã 3ïŒãã¡ã€ã³ããžãã¯ãçŽç²é¢æ°ã§è¡šçŸããããšã«ããå¯èªæ§ã»ãã¹ãå®¹ææ§ã®åäž ãŸãšã ãããã« Tanstack Query ã«ã€ã㊠Tanstack Query ã¯ãWeb ã¢ããªã±ãŒã·ã§ã³ã®ãµãŒãç¶æ
ã®ååŸããã£ãã·ã¥ãåæãæŽæ°ãç°¡åã«è¡ãããšãã§ããã©ã€ãã©ãªã§ãã 1 é¡äŒŒã©ã€ãã©ãªã«ã¯ SWRãApollo ClientãRTK Query çãæããããŸãã 2 ç§ãã¡ã¯ãæ©èœæ§ã»ããã¥ã¡ã³ãã®å
å®åºŠã»ã³ãã¥ããã£ã®å°æ¥æ§ãç·åçã«å€æããçµæãTanstack Query(React Query)ãæ¡çšã㊠React ã³ãŒãããŒã¹ã®åæ§ç¯ãé²ããŠããŸãã å°å
¥èæ¯ã®è©³çްã¯ãåŸã®ã»ã¯ã·ã§ã³ã§è©³ãã玹ä»ããŸãã Tanstack Query ãæŽ»çšããããã³ããšã³ãã¢ãŒããã¯ã㣠ããã§ã¯æ¬é¡ã® Tanstack Query ãæŽ»çšããããã³ããšã³ãã¢ãŒããã¯ãã£ã«ã€ããŠç޹ä»ããŸãã å§ãã«ãã¢ãŒããã¯ãã£ã®å
šäœåã瀺ããæ¬¡ã®å³ãã芧ãã ããã ãŸã泚ç®ããŠé ããããã€ã³ãã¯ãBackend ãš View ã®éã«ãã Resource Operation ã§ãã Resource Operation 㯠Tanstack Query ã䞻軞ã«å®è£
ãããŠããã¬ã€ã€ãŒã§ãã 圹å²ã倧ããåé¡ãããšæ¬¡ã® 2 ã€ãæããããŸãã éåæç¶æ
管çã«é¢é£ããå®è£
ã®éçŽ Backend ã§æ±ãããŒã¿(OpenAPI ã¹ããŒã)ãš View ã§æ±ãããŒã¿(ViewModel)ã®çžäºå€æ å
šäœåãææ¡ããããã«ãå·Šå³ã®ã¬ã€ã€ãŒã«ã泚ç®ããŠãã ããã å³ã® Backend 㯠CLINICS ã§ã¯ Ruby on Rails ã§å®è£
ããã REST API ãæäŸããã¢ããªã·ãã¯ãªãµãŒãã§ãã API ã§æ±ãã¹ããŒã㯠OpenAPI ã䜿ã£ãŠå®çŸ©ããŠããŸãã OpenAPI ã¹ããŒãã¯ã committee-rails ã䜿ã£ãã¬ã¹ãã³ã¹æ€èšŒãšã openapi-generator ã䜿ã£ã API ã¯ã©ã€ã¢ã³ãã³ãŒãã®èªåçæã«æŽ»çšããŠããŸãã å·Šã® View 㯠CLINICS ã§ã¯ React ã§å®è£
ãããã³ã³ããŒãã³ãã«ãªããŸãã View ã§ã¯ Backend ã® OpenAPI ã¹ããŒããçŽæ¥åç
§ããããšãé¿ãã ViewModel ãšåŒã°ããããã³ããšã³ãã§å®çŸ©ããã¢ãã« 3 ã®ã¿ãåç
§ããèšèšãšããŠããŸãã Resource Operation 㯠View ãš Backend ã®å¢çã¬ã€ã€ãŒãšããŠéåæç¶æ
管çãšããŒã¿ã®çžäºå€æãè¡ãã·ã³ãã«ãªã¬ã€ã€ãŒãšãªã£ãŠããŸãã ç¶ããŠãäžèšã®éçšã®ããã®å®è£
詳现ã玹ä»ããŸãã Resource Operation ã®å®è£
ãã£ã¬ã¯ããªæ§æ Resource Operation ã¬ã€ã€ãŒã§ã¯ããªãœãŒã¹ããšã«éåæç¶æ
管çãšããŒã¿ã®çžäºå€æã®åŠçããŸãšããŠããŸãã ãã£ã¬ã¯ããªããªãŒã§ Resource Operation ã¬ã€ã€ãŒå
šäœã衚çŸãããšæ¬¡ã®ããã«ãªããŸãã src/resourceOperations âââ resourceTagName # äŸïŒ todo â âââ cache.ts â âââ mutations.ts â âââ queries.ts âââ resourceGroupTagName # äŸïŒ systemSettingsïŒãªãœãŒã¹ã«éå±€ãããå ŽåïŒ âââ resourceTagName # äŸïŒ organizationïŒãªãœãŒã¹ã°ã«ãŒãé
äžã®ãªãœãŒã¹ïŒ âââ cache.ts âââ mutations.ts âââ queries.ts ãã£ã¬ã¯ããªåã® resourceTagName ãš resourceGroupTagName 㯠OpenAPI ã¹ããŒãã§ãšã³ããã€ã³ãããšã«å²ãæ¯ãããŠãã tag ãå
ã«åœåããŠããŸãã ãã£ã¬ã¯ããªããšã« 次㮠3 ã€ã®ãã¡ã€ã«ãå®çŸ©ããŠããŸãã ãã¡ã€ã« åœ¹å² cache.ts Query Key ã®å®çŸ©ããã£ãã·ã¥æäœã®ããã®ã«ã¹ã¿ã ããã¯ã®å®çŸ© queries.ts useQuery ãã©ããããã«ã¹ã¿ã ããã¯ã®å®çŸ©ãAPI Request/Response ã®æŽåœ¢ mutations.ts useMutation ãã©ããããã«ã¹ã¿ã ããã¯ã®å®çŸ©ãAPI Request ã®æŽåœ¢ cache.ts cache.ts ã¯æ¬¡ã®ããã«å®è£
ããŠããŸãã // â Query Key // queries.ts ãããå Žåã«å¿
èŠã«å¿ããŠå®£èš export const todoKeys = { all: [ "todo" ] as const , list : () => [... todoKeys . all , "list" ] as const , paginateList : ( page ?: number ) => [... todoKeys . list (), { page }] as const , detail : ( id : string ) => [... todoKeys . all , "detail" , id ] as const , }; // â¡ ãã£ãã·ã¥æäœã®ããã®ã«ã¹ã¿ã ãã㯠// mutations.ts ãããå Žåã«å¿
èŠã«å¿ããŠå®£èš export function useTodoCache () { const queryClient = useQueryClient (); return useMemo ( () => ({ invalidateList : () => queryClient . invalidateQueries ( todoKeys . list ()), invalidateDetail : ( id : string ) => queryClient . invalidateQueries ( todoKeys . detail ( id )), }), [ queryClient ] ); } â Query Key ã¯ã useQuery ã® queryKey ãªãã·ã§ã³ã«äžããå€ãå®çŸ©ãã宿°ã§ããããã¯åŸè¿°ã® queries.ts ã§å©çšããŸãã å®è£
㯠Tanstack Query ã¡ã³ããã® Dominik ããã®ããã°ã§ç޹ä»ãããŠãã Query Key factories ãã¿ãŒã³ã䜿ã£ãŠããŸãã ãã£ã¬ã¯ããªåã all ã®å€ãšããããšã§ãå€§èŠæš¡ãªã¢ããªã±ãŒã·ã§ã³ã«ãããŠãããŒã®è¡çªãé²ãããšãå¯èœã§ãã â¡ ãã£ãã·ã¥æäœã®ããã®ã«ã¹ã¿ã ããã¯ã§ã¯ QueryClient ãå©çšãããã£ãã·ã¥æäœããŸãšããŠããŸããããã¯åŸè¿°ã® mutations.ts ã§å©çšããŸãã CLINICS ã§ã¯ æ¥œèŠ³çæŽæ° ãããªãæ¹éãšããŠããããã invalidateQueries ãå®è¡ãã颿°çŸ€ã®ã¿ãå®çŸ©ããŠããŸãã queries.ts queries.ts ã¯æ¬¡ã®ããã«å®è£
ããŠããŸãã import { type Todo } from "@/viewModels/todo" ; import { todoApi , type GetTodoDetailRequest } from "@/api/generated" ; // ⢠queryFn export const query = { getTodoList : async (): Promise < Todo []> => { const { data } = await todoApi . getTodoList (); return data . todoList ; }, getTodoDetail : async ( request : GetTodoDetailRequest ): Promise < Todo > => { const { data } = await todoApi . getTodoDetail ( request ); return data . todo ; }, }; // ⣠Request Selector export const request = { getTodoDetail : ( id : string | undefined ): GetTodoDetailRequest => { if ( id === undefined ) { // `enabled: false` ãšãªãæ¡ä»¶ã®åŒæ°ãäžããããå ŽåäŸå€ãšããããšã§ã // queryFn å
ã®åã®æŽåæ§ãä¿ã€ throw new InvalidRequestParameterError ( "Required parameter id was undefined when calling request.getTodoDetail." ); } return { id , }; }, }; ⢠queryFn ã¯ãAPI ãžã®ãªã¯ãšã¹ãã責åãšãã颿°çŸ€ã§ãã ããã§äœ¿çšããŠãã ApiClient ã Request ã®å㯠openapi-generator ããçæããŠããŸãã queryFn ã®æ»ãå€ã®åã¯ãåŸè¿°ã® ViewModel ã§å®çŸ©ããåãæç€º ããŠããŸãã ãã®ããã«ããã³ããšã³ãåŽã§ãµãŒãã¹ããŒãã®åãå¥éå®çŸ©ããããšã§ãããã³ããšã³ããšããã¯ãšã³ãã忥ããŠå®è£
ããéã«éçºãããããªãã¡ãªããããããŸãã çç¥ããŠããŸãããã¬ã¹ãã³ã¹ã«å¿ãããšã©ãŒã® throw ãããã§è¡ããŸãã ⣠Request Selector 㯠View ããåãåã£ãå€ããªã¯ãšã¹ããã©ã¡ãŒã¿ãžãããã³ã°ããããšã責åãšãã颿°çŸ€ã§ãã useQuery ã®æ¡ä»¶ä»ãå®è¡ãå¶åŸ¡ãã enabled ãªãã·ã§ã³ã§ false ãšãªãæ¡ä»¶ãäŸå€ãšããããšã§ãqueryFn å
ã§ åã¬ãŒããããªããŠè¯ãèšèšãšããŠããŸãã ãããã䜿ã£ãŠ View ãšã®ã€ã³ã¿ãŒãã§ãŒã¹ãšãªã useQuery ã©ãããŒãå®çŸ©ããŸãã // †Base Query // API Response ãæŽåœ¢ããã«è¿ã export type UseTodoDetailQueryProps < QueryResult > = { id : string | undefined ; select ?: ( data : Todo ) => QueryResult ; }; export function useTodoDetailQuery < QueryResult = Todo >( props : UseTodoDetailQueryProps < QueryResult > ) { return useQuery ({ queryKey: todoKeys . detail ( props . id ), queryFn : () => { return query . getTodoDetail ( request . getTodoDetail ( props . id )); }, enabled: !! props . id , select: props . select , useErrorBoundary: true , }); } import { selectTodoForm } from "@/viewModels/todo/todoForm" ; // ⥠Selector Query // API Response ã View ã§åç
§ããããã©ãŒãããã«æŽåœ¢ããŠè¿ã export function useInitialTodoFormQuery () { // Base Query ã« select ãªãã·ã§ã³ãäžãã return useTodoDetailQuery ({ select: selectTodoForm , // selectTodoForm 㯠Todo ã TodoForm ã«å€æãã ViewModel SelectorïŒåŸè¿°ïŒ }); } useQuery ã©ãããŒã¯ã†Base Query 㚠⥠Selector Query ã«åããŠå®çŸ©ããŠããŸãã CLINICS ã§ã¯åãããŒã¿ãœãŒã¹ã«å¯ŸããŠç»é¢ããšã«ç°ãªããã©ãŒãããã§è¡šç€ºããããšãå€ããããŸãã Selector Query ã§ ä»»æã®ãã©ãŒãããã«æŽåœ¢ããããšã§ãç»é¢ããšã«æé©åãããããŒã¿ã®ååŸãã¹ã±ãŒã©ãã«ã«å®çŸããŠããŸãã 4 å ããŠããã®ææ³ã§ã¯ Selector ã«ãã¡ã€ã³ããžãã¯ãåéãããããã Selector ãéç¹çã«ãã¹ãããããšã§å質ãæ
ä¿ãããã ã¡ãªããããããŸãã Query ã®ãšã©ãŒå¶åŸ¡ã¯ useErrorBoundary ãªãã·ã§ã³ã䜿ã£ãŠ Error Boundary ã衚瀺ããæ¹éãšããŠããŸãã mutations.ts mutations.ts ã¯æ¬¡ã®ããã«å®è£
ããŠããŸãã import { type TodoForm } from "@/viewModels/todo/todoForm" ; import { todoApi , type PostTodoRequest } "@/api/generated" ; // ⊠mutationFn export const mutation = { createTodo : ( request : PostTodoRequest ) => { return todoApi . postTodo ( request ); }, }; // â§ Request Selector // ViewModel ãã API Request ãžå€æãã export const request = { createTodo : ( todoForm : TodoForm ): PostTodoRequest => { return { PostTodoRequest : { todo : { title : todoForm . title , description : todoForm . description , status : todoForm . status , favorite : todoForm . favorite === "true" ? true : false , }, }, }; }, }; // âš Custom Mutation export function useCreateTodoMutation () { const { invalidateList } = useTodoCache (); return useMutation ({ mutationFn : ( todoForm : TodoForm ) => { return mutation . createTodo ( request . createTodo ( todoForm )); }, onSuccess : () => { return invalidateList (); }, }); } åºæ¬çãªæ§æã¯ queries.ts ãšåãã§ãã mutations.ts ã§ã queries.ts ãšåæ§ã« â§ Request Selector ãå®çŸ©ããŸãã Request Selector ã«ã¯ãããŒã¿æŽåœ¢ãæ±ããããã¡ã€ã³ããžãã¯ãéãŸããããã§ãã ãã®ãããå
¥åºåãå¢çå€ãäŸå€ã®ãã¹ããç©æ¥µçã«æžããŠåè³ªã®æ
ä¿ã«ç¹ããŠããŸãã Mutation ã®ãšã©ãŒã¯ç»é¢ããšã« UI ã§ãã£ãŒãããã¯ãããããã³ã³ããŒãã³ãåŽã§å¶åŸ¡ããŠããŸãã Resource Operation ã¬ã€ã€ãŒã«é¢ããå®è£
ã®ç޹ä»ã¯ä»¥äžã§ãã ViewModel ã®å®è£
ViewModel ãšã¯ãView(React Component) ã§æ±ãããŒã¿ã®ã¹ããŒããšåã§ãã Resource Operation ã®å®è£
ã§ã¯ãAPI Response ã ViewModel ã«æŽåœ¢ã㊠View ã«æäŸããããšã玹ä»ããŸããã å
šäœã®çè§£ãæ·±ãããããViewModel ã®å®è£
ã玹ä»ããŸãã ãã£ã¬ã¯ããªæ§æ ViewModel ã¯é¢å¿ãåé¢ãããã Resource Operation ãšã¯å¥ã®ãã£ã¬ã¯ããªã«å®çŸ©ããŠããŸãã src/viewModels âââ todo âââ todo.ts # ãµãŒãã¹ããŒã âââ todoForm.test.ts # ããã³ããšã³ãã«éããã¹ããŒãã®ãã¹ã âââ todoForm.ts # ããã³ããšã³ãã«éããã¹ããŒã âââ todoSearchCondition.ts # ããã³ããšã³ãã«éããå ViewModel ã§å®çŸ©ããã¹ããŒãã»åã¯å€§ããåã㊠3 ã€ã«åé¡ãããŸãã ãµãŒãã¹ããŒã ããã³ããšã³ãã«éããã¹ããŒã ããã³ããšã³ãã«éããå ãµãŒãã¹ããŒã ãµãŒãã¹ããŒãã¯ã API Response ãšããŠæåŸ
ããããŒã¿ã®ã¹ããŒãåã³åã§ãã åè¿°ã® queries.ts å
㮠⢠queryFn ã§äœ¿çšããŸãã zod ã䜿ã£ãŠæ¬¡ã®ããã«å®è£
ããŠããŸãã // src/viewModels/todo/todo.ts // enum 㯠View ã§ <option value={todoStatus.enum.ready} /> ã®ããã«äœ¿çšãã export const todoStatus = z . enum ([ "ready" , "doing" , "done" ]); // â© ãµãŒãã¹ããŒãã®ã¹ããŒã // éçºäžã®ã¿ ⢠queryFn å
ã§ API Response ã parse ããããšã§ãã¹ããŒãã®äžæŽåãæ€åºããããã®è£å©èŒªãšããŠäœ¿çšãã export const todo = z . object ({ id: z . string (), title: z . string (), description: z . string (). nullable (), status: todoStatus , favorite: z . boolean (), }); // ⪠ãµãŒãã¹ããŒãã®å // ⢠queryFn ã®æ»ãå€ã®åãšããŠäœ¿çšãã export type Todo = z . infer < typeof todo >; â© ãµãŒãã¹ããŒãã®ã¹ããŒãã¯ã⪠ãµãŒãã¹ããŒãã®åã®çæãšéçºæã®ã¹ããŒãæ€èšŒã«äœ¿çšããŠããŸãã éçºæã®ã¿ API Response ãæ€èšŒããããšã§ãâ© ãµãŒãã¹ããŒãã®ã¹ããŒããš Open API ã¹ããŒãã®æŽåæ§ã確èªããŠããŸãã // src/resourceOperations/todo/queries.ts import { todo , type Todo } from "@/viewModels/todo" ; export const query = { getTodoList : async (): Promise < Todo []> => { const { data } = await todoApi . getTodoList (); // éçºæãAPIãšã®çµåã¿ã€ãã³ã°ã§æ€èšŒããŠããã³ããšã³ããšããã¯ãšã³ãã§ã¹ããŒãã®éœéœ¬ããªãããšã確èªãã // æ€èšŒãæžãã ã parse åŠçãå€ã return data . todoList . map (( x ) => todo . parse ( x )); }, }; äŸå€ãšããŠã å€éšãµãŒãã¹ããååŸããããŒã¿ã«é¢ããŠã¯åžžã«ãµãŒãã¹ããŒãã®ã¹ããŒãã䜿ã£ãŠæ€èšŒ ããŠããŸãã äŸãã°ãå€éšãµãŒãã¹ã®ä»æ§ãšããŠé·ãã 1 以äžã®é
åãè¿ã£ãŠãããšæ±ºãŸã£ãŠããŠãããã³ããšã³ãåŽããã®ä»æ§ã«åºã¥ããåŠçãå®è£
ããŠããå Žåã z.array().min(1) ã®ã¹ããŒãã§åžžã«æ€èšŒããŸãã ãããã¯ãŒã¯ã«è¿ãç®æã§äžæ£ãªããŒã¿ãæ€åºããããšã§ãäŸå€çºçæã®èª¿æ»ã容æã«ããã¡ãªããããããšèããŠããŸãã ããã³ããšã³ãã«éããã¹ããŒã ããã³ããšã³ãã«éããã¹ããŒãã¯ããã®ã»ãšãã©ããã©ãŒã ã®ããªããŒã·ã§ã³ã¹ããŒãã§ãã ãã¡ãã zod ã䜿ã£ãŠå®çŸ©ããŠããŸãã // src/viewModels/todo/todoForm.ts import { type Todo , todo } from "./todo" ; // ããã³ããšã³ãã«éããã¹ããŒã // ãã©ãŒã ã®ã¹ããŒã㯠react-hook-form ãšé£æºããŠäœ¿çšããïŒçç¥ïŒ export const todoForm = z . object ({ title: z . string () . min ( 1 , "å
¥åããŠãã ãã" ) . max ( 200 , "200å以å
ã§å
¥åããŠãã ãã" ), description: z . string (). max ( 500 , "500å以å
ã§å
¥åããŠãã ãã" ), status: todo . shape . status , favorite: z . union ([ z . literal ( "true" ), z . literal ( "false" )]), }); export type TodoForm = z . infer < typeof todoForm >; // â« ViewModel Selector // ãµãŒãã¹ããŒãããããã³ããšã³ãã«éããã¹ããŒããžå€æãã // åè¿°ã® queries.ts å
⥠Selector Query ã§äœ¿çšãã export function selectTodoForm ( todo : Todo ): TodoForm { return { title: todo . title , description: todo . description ?? "" , status: todo . status , favorite: todo . favorite ? "true" : "false" , }; } ããã³ããšã³ãã«éããã¹ããŒãã¯ãå¿
ããµãŒãã¹ããŒããå
ã«çæããéçšãšããŠããŸãã ãã®ããã«ãããã³ããšã³ãã«éããã¹ããŒãã宣èšããçŽäžã«ããµãŒãã¹ããŒãããããã³ããšã³ãã«éããã¹ããŒããžå€æãã â« ViewModel Selector ãå®çŸ©ããŸãã null 㮠空æåãžã®å€æãæéããŒã¿ã®ãã©ãŒãããã®ãããªã ãµãŒãã¹ããŒã ãš View ã§äœ¿ãå€ã®å·®ååžåã¯ãã®ã»ã¬ã¯ã¿å
ã§è¡ããŸãã ãã®ããã«ã ViewModel ã¬ã€ã€ãŒã§ã¯ä»ã®ã¬ã€ã€ãŒãšäŸåããªãããã«ãã¡ã€ã³ããžãã¯ãè¡šçŸ ããŠããŸãã ãã¡ã€ã³ããžãã¯ã Tanstack Query ã«äŸåããªãããšã§ãä»åŸæè¡åºç€ãå·æ°ããå Žåã§ã圱é¿ãæå°éã«çããããšãçããšããŠããŸãã ããã³ããšã³ãã«éããå äŸåé¢ä¿ãæŽãããããResource Operation ãš View ããåç
§ããããã³ããšã³ãã«éããå㯠viewModels é
äžã«å®£èšããŠããŸãã // src/viewModels/todo/todoSearchParams.ts export type TodoSearchCondition = { sort ?: "created_at_asc" | "created_at_desc" ; }; ViewModel ã«é¢ããå®è£
ã®ç޹ä»ã¯ä»¥äžã§ãã Tanstack Query å°å
¥ã®èæ¯ãšã¢ãŒããã¯ãã£ã®çã èæ¯ïŒã³ãŒãå質ã®èª²é¡ Tanstack Query ãå°å
¥ãã以åã® CLINICS ã®éåæåŠçåšèŸºã®ã³ãŒãããŒã¹ã§ã¯ããã€ãã®èª²é¡ããããŸããã éçºäœéšã®èª²é¡ éåæåŠçã®ããã®ãããã ãªåºç€ããã¯ãç¬èªã«å®è£
ããŠãã 5 ããšã«ãããéçºäœéšã®èгç¹ã§æ¬¡ã®ãããªèª²é¡ããããŸããã æ°ããèŠä»¶ïŒããŒãªã³ã°ã»ç¡éèªã¿èŸŒã¿çïŒãçºçããéã«ãæåã«æ
åœããéçºè
ãéœåºŠæ©èœæ¡åŒµããå¿
èŠããã ãã¹ããå®è£
ãããŠããªãã£ããã倿޿ã«å質確èªã®è² æ
ã倧ãã åŠç¿å®¹ææ§ã®èª²é¡ äžè¿°ã®åºç€ããã¯ã«ããã¥ã¡ã³ãããªãã£ããããåŠç¿å®¹ææ§ã®èгç¹ã§æ¬¡ã®ãããªèª²é¡ããããŸããã åºç€ããã¯ã«ããã¥ã¡ã³ãããªããæ°èŠã¡ã³ããŒã®åŠç¿ã³ã¹ããé«ã æ§ã
ãªå®è£
ææ³ãæ··åšããããšã§ãå®è£
æã«è¿·ããçããŠãã å¯èªæ§ã®èª²é¡ CLINICS ã¯éçºéå§ããçŽ 7 幎以äžãçµéããŠããŸãã ãã®äžã§ãããã³ããšã³ãã¯æè¡ãã¬ã³ãã®å€åãæ©ããããæ§ã
ãªã©ã€ãã©ãªããã¿ãŒã³ã䜿ã£ãŠå®è£
ãããŠããŸãã ç¹ã«è¿å¹ŽããæŒžé²çã«å°å
¥ãã React ã䜿ã£ãå®è£
ã«ã€ããŠã¯ãæç¢ºãªèšèšã確ç«ãããŠããŸããã§ããã ãããã®èæ¯ããããŒã ã§å®å®ããã¢ãŠãããããåºãããšãå°é£ã§ãå¯èªæ§ã®èгç¹ã§æ¬¡ã®ãããªèª²é¡ããããŸããã ç»é¢ã«ãã£ãŠããã¯ã颿°ã®ç²åºŠãå®çŸ©å Žæãéãããšã§ã³ãŒããªãŒãã£ã³ã°ã®è² è·ãé«ã ã³ãŒãã¬ãã¥ãŒæã®ã¬ãã¥ã¯ãŒã®è² æ
ã倧ãã ãã¹ãå®¹ææ§ã®èª²é¡ å€ãã®ãã¡ã€ã³ããžãã¯ãã³ã³ããŒãã³ããã«ã¹ã¿ã ããã¯å
ã«æ··åšããŠããããšã§ããã¹ãå®¹ææ§ã®èгç¹ã§æ¬¡ã®ãããªèª²é¡ããããŸããã ãã¹ããå®è£
ãã¥ãã ãã¹ãã«ãã¬ããžãäœã CLINICS ã¯ãªã³ã©ã€ã³èšºçã»é»åã«ã«ãçã®å»çæ©é¢æ¥åãæ¯ããæ©èœãæäŸãã SaaS ãã©ãããã©ãŒã ã§ãã ä»åŸé·ãã«æž¡ã£ãŠå€ãã®å»çæ©é¢ã®æ¹ã
ã« CLINICS ãå©çšããŠé ãããã«ã¯ã ã³ãŒããèªã¿ãããã倿Žãããããç¶æããããç¶æ
ã«ä¿ã¡ç¶ããããš ãéèŠã§ãã åã®ç« ã§ç޹ä»ããã¢ãŒããã¯ãã£ã¯ æç¶å¯èœãªéçºãç®æã㊠èšèšããŸããã å
·äœçã«ã¯ã éçºäœéšã»åŠç¿å®¹ææ§ã»å¯èªæ§ã»ãã¹ãå®¹ææ§ãåäžããããšãçã ãšããŠããŸãã çã 1ïŒTanstack Query ã®å°å
¥ã«ããéçºäœéšã®åäž èæ¯ã§èª¬æãããšãããTanstack Query å°å
¥ä»¥åã¯ç¬èªå®è£
ããããã¯ã䜿ã£ãŠéåæåŠçãå®è£
ããŠããŸããã Tanstack Query ãå°å
¥ãããã£ããã¯ãããŒã å
ã§ã®éè«ã®äžã§ãç¬èªå®è£
ã®ããã¯ã䜿ãã¥ãããšãã声ãæãã£ãããšã§ãã ããã§ãç¬èªå®è£
ã®ããã¯èªäœã®è³ªãé«ãããã質ã®é«ãã©ã€ãã©ãªãå°å
¥ããããè°è«ããçµæãæ¬¡ã®çç±ã§ã©ã€ãã©ãªãå°å
¥ããæ±ºå®ãããŸããã æ¥µåèªåãã¡ã§ã³ãŒããæžããã«éåæåŠçã»ç¶æ
管çã®å®è£
ãå®çŸããã å®è£
ã«å°ã£ããšãã«ããã¥ã¡ã³ããèªãã°è§£æ±ºããç°å¢ã«ããã CLINICS ã§ã¯æè¡çãªèæ¯ 6 ãã Tanstack Query ãš SWR ãåè£ã«äžãããŸãããã select ãªãã·ã§ã³ã invalidateQueries ã® Partial Query Matching çã®æ©èœæ§ãšãããã¥ã¡ã³ãã®å
å®åºŠåãã®èгç¹ãã Tanstack Query ãæ¡çšããŸããã Tanstack Query ãæ¡çšããããšã§ã éåæåŠçã®ããã®ã³ãŒãã®èšè¿°éã倧å¹
ã«åæž ãããã»ããããŒã¿ã®ç¹æ§ã«å¿ã㊠Query ããšã«ãã£ãã·ã¥ã®æéã調æŽããããšãå¯èœãšãªãéçºäœéšã倧å¹
ã«åäžããŸããã çã 2ïŒã·ã³ãã«ãªã¬ã€ã€ãŒåå²ã«ããåŠç¿å®¹ææ§ã®åäž CLINICS ã§ã¯ãäºæ¥ã®æ¡å€§ã«ãšããªãã³ãŒãããŒã¹ã«é¢ãããšã³ãžãã¢ãå¢ãç¶ããŠããŸãã å®è£
ã®é²ãæ¹ã¯ãããžã§ã¯ãã«ãã£ãŠæé©ãªåœ¢åŒãéžæããŠããŸãã ããã¯ãšã³ãããããã³ããšã³ããŸã§äžæ°é貫ã§å®è£
æè¡é åã«åããŠåæ¥ ãã®ããã«å€ãã®ãšã³ãžãã¢ãæ§ã
ãªåœ¢ã§é¢ããç°å¢ã§ã¯ãã³ãŒãããŒã¹ã®åŠç¿å®¹ææ§ãé«ããå®è£
ããã³ãŒãã¬ãã¥ãŒã®å®äºãŸã§ãã¹ã ãŒãºã«è¡ããããšãéèŠã§ãã åã®ç« ã§ç޹ä»ããã¢ãŒããã¯ãã£ã¯ã ã¬ã€ã€ãŒåå²ãã·ã³ãã«ã«ããããšã§ã³ãŒãããŒã¹ã«æ
£ãããŸã§ã®æéãæå°éã«ãã ããšãæèããŠããŸãã å ããŠã å®è£
ãã¿ãŒã³ãå®åœ¢åããããšã§ãã³ãŒãããŒã¹ã«éŠŽæã¿ããªããŠãè¿·ããªãå®è£
ã§ãã ã»ãã颿°ã®ç²åºŠãçµ±äžãããããšã§ãã³ãŒãã¬ãã¥ãŒã®è² è·è»œæžã«ãç¹ãã£ãŠããŸãã ã¬ã€ã€ãŒåå²ã®ç²åºŠãå®è£
ãã¿ãŒã³ã«ã€ããŠã¯ã次ã®èšäºãåèã«ãããŠé ããŸããã ããã³ããšã³ãã¢ãŒããã¯ãã£ã®è©±: Resource Set ã®çŽ¹ä» ã»ãã«ã CLINICS ã§ã¯åŠç¿å®¹ææ§ã®åäžã®åãçµã¿ãšããŠã æ°ããã©ã€ãã©ãªãã¢ãŒããã¯ãã£ãå°å
¥ããéã¯å匷äŒãéå¬ ããŠã©ã€ãã©ãªã®åºæ¬çãªäœ¿ãæ¹ãé »åºã®å®è£
ãã¿ãŒã³ã«é¢ããç¥èŠãå
±æããŠããŸãã çã 3ïŒãã¡ã€ã³ããžãã¯ãçŽç²é¢æ°ã§è¡šçŸããããšã«ããå¯èªæ§ã»ãã¹ãå®¹ææ§ã®åäž ã¢ãŒããã¯ãã£ãå·æ°ãã以åã¯ãå€ãã®ãã¡ã€ã³ããžãã¯ãã³ã³ããŒãã³ããã«ã¹ã¿ã ããã¯å
ã«æ··åšããŠããããšã§ãå¯èªæ§ããã¹ãå®¹ææ§ã«æ¯éãããããŠããŸããã CLINICS ã®ããã³ããšã³ãã«ã¯ãå»çã·ã¹ãã ã«é¢ããè€éãªãã¡ã€ã³ããžãã¯ãå€ãããã ã·ã³ãã«ã§ãã¹ãããããã³ãŒãããŒã¹ ãäœã£ãŠããããšããšãããéèŠã ãšèããŠããŸãã ãã®èª²é¡ã¯ããã¡ã€ã³ããžãã¯ãéãŸãåŸåã«ãã queries.tsã mutations.ts ã® Request Selector ã ViewModel Selector ã®å®è£
ãå®ååããçŽç²é¢æ°ã§è¡šçŸããããšã«ãã解決ããŸããã ãŸãšã Tanstack Query ã䜿ã£ãããã³ããšã³ãã¢ãŒããã¯ãã£ã®å®äŸã玹ä»ããŸããã Resource Operation ã¬ã€ã€ãŒã« Tanstack Query ã®å®è£
ãå®ååããŠéçŽããŠããŸãã ã¬ã€ã€ãŒåå²ãã·ã³ãã«ã«ããå®è£
ãå®ååããããšã§ãéçºçµç¹ã®ã¹ã±ãŒã«ã«å¯Ÿå¿ããŠããŸãã useQuery ã® select ãªãã·ã§ã³ã䜿ããã¹ã±ãŒã©ãã«ã«ããŒã¿å€æåŠçãèšè¿°ããŠããŸãã ããŒã¿å€æåŠçã«ã¯ãã¡ã€ã³ããžãã¯ãéãŸããããããããªãã¹ãå°ããç²åºŠã®çŽç²é¢æ°ã§è¡šçŸããããšã§ãå¯èªæ§ã»ãã¹ãå®¹ææ§ã®åäžãçã£ãŠããŸãã ãã®èšäºã®å
容ã Tanstack Query ã®å°å
¥ãèããŠããæ¹ã®åèã«ãªãã°å¹žãã§ãã ãããã« CLINICS ã§ã¯ãæ©èœéçºãšäžŠè¡ããŠããã³ããšã³ãåºç€ãæ¹åããåãçµã¿ã宿œããŠããŸãã Redux ãã Tanstack Query ãžã®ç§»è¡ UI ã©ã€ãã©ãªã® Mithril ãã React ãžã®ç§»è¡ 7 ãã¶ã€ã³ã·ã¹ãã ã®æ§ç¯ãšãããã¯ããžã®åæ ãã®ãããªåãçµã¿ã«èå³ãããæ¹ã¯æ¬¡ã®ãªã³ã¯ããæ¯éãé£çµ¡ãã ããã åéã®äžèЧ | æ ªåŒäŒç€Ÿã¡ãã¬ãŒ ã¡ãã¬ãŒã®æ¡çšæ
å ±ã¯ãã¡ãããã確èªãã ããã www.medley.jp Footnotes Overview | TanStack Query Docs â© Comparison | React Query vs SWR vs Apollo vs RTK Query vs React Router | TanStack Query Docs â© ãã®èšäºã§ç޹ä»ããŠãã ViewModel 㯠View ã¬ã€ã€ãŒå°çšã®ã¢ãã«ãè¡šãæŠå¿µã§ãã MVVM ã¢ãŒããã¯ãã£ã® ViewModel ãšã¯ç°ãªããŸãã â© Tanstack Query ã«ãããããŒã¿å€æææ³ã®è©³çŽ°ã¯ React Query Data Transformations | TkDodoâs blog ã§ç޹ä»ãããŠããŸãã â© Tanstack Query å°å
¥ä»¥åã®éåæåŠç㯠useAsync React Hook - useHooks ãã«ã¹ã¿ãã€ãºããããã¯ã䜿ã£ãŠå®è£
ããŠããŸããã â© CLINICS ã§ã¯ äžéšã®å®è£
ç®æã§ Redux ã䜿çšããŠããŸããã RTK Query ã¯ä»åæ¡çšããã©ã€ãã©ãªã®åè£ããé€å€ããŸãããããã¯ãçŸåšäœ¿çšããŠãã Redux ã®ããŒãžã§ã³ãäœããã¬ã¬ã·ãŒãªåšèŸºã©ã€ãã©ãªãè€æ°äœ¿çšããŠããèæ¯ã§ Redux Toolkit ãžã®ç§»è¡ã«çžåœãªå·¥æ°ãå¿
èŠãšããããã§ãã â© 2023 幎 3 æçŸåšã CLINICS ã®ããã³ããšã³ãã® çŽ 50% ã¯ã Mithril ãš Redux ã§æ§æãããŠããŸããéçºäœéšã®åäžã®ããã React ãžã®å®å
šç§»è¡ãç®æããŠæ¥ã
æ¹åãç¶ããŠããŸãã â©