ปรับปรุงชั้นบริการของคุณในโปรเจ็กต์ชุดเครื่องมือ Redux
วิธีสร้างแอป React ด้วย RTK-QUERY
คู่มือสำหรับผู้เริ่มต้นในการเขียนแบบสอบถามด้วย rtk-query
เป็นไปไม่ได้เลยที่จะเป็นนักพัฒนาส่วนหน้าที่ทำงานร่วมกับ React และไม่เคยได้ยินเกี่ยวกับ Redux มาก่อนในชีวิตของคุณ บริษัทส่วนใหญ่ใช้ Redux ในโค้ดเบสของตน และการรู้แนวคิดพื้นฐานของ Redux ได้กลายเป็นหนึ่งในแก่นแท้ของการเป็น Frontender มีการพูดคุยกันมากมายเกี่ยวกับการใช้หรือไม่ใช้กับผลิตภัณฑ์ของคุณ ความจริงก็คือคุณไม่สามารถหาเครื่องมือที่สมบูรณ์แบบสำหรับทุกโครงการได้ คุณควรตัดสินใจว่าเครื่องมือใดเหมาะสมที่สุดสำหรับวัตถุประสงค์และผลิตภัณฑ์ของคุณ แม้ว่าเครื่องมือแต่ละอย่างจะมีข้อดีและข้อเสียอยู่บ้าง แต่สิ่งที่สำคัญที่สุดคือการแลกเปลี่ยนระหว่างข้อดีและข้อเสียของเครื่องมือสำหรับคุณ
ข้อความค้นหา RTK เป็นเครื่องมือดึงข้อมูลและแคชที่มีประสิทธิภาพ ได้รับการออกแบบมาเพื่อลดความซับซ้อนของกรณีทั่วไปสำหรับการโหลดข้อมูลในเว็บแอปพลิเคชัน โดยไม่จำเป็นต้องเขียนตรรกะในการดึงข้อมูลและแคชด้วยตนเอง
หากคุณใช้ redux โดยเฉพาะ redux-toolkit คุณรู้อยู่แล้วว่า rtk-query มาพร้อมกับการพึ่งพาในตัว มีเครื่องมือที่คล้ายกันมากมาย เช่น react-query แต่เมื่อคุณใช้ redux-toolkit และมี rtk-query อยู่ในบันเดิลของคุณอยู่แล้ว ทำไมไม่ลองใช้มันดูล่ะ? ประโยชน์บางประการของ trk-query คือ:
- มันเข้ากันได้อย่างสมบูรณ์กับ redux
- เขียนด้วย TypeScript
- ปรับปรุงชั้นบริการของคุณให้สามารถนำมาใช้ซ้ำได้มากขึ้น และทำให้โครงการของคุณมี hooks ที่สร้างขึ้นอัตโนมัติขึ้นไปอีกระดับ
- ติดตามและสังเกตได้ผ่าน redux-devtools
- มันมีพฤติกรรมการแคชที่ดีจริงๆ
ในบทความนี้ ฉันจะช่วยคุณสร้างแอปพลิเคชัน React ของบล็อกง่ายๆ ด้วย rtk-query และ typescript เพื่อให้คุณคุ้นเคยกับเครื่องมือที่ยอดเยี่ยมนี้มากขึ้น แอปพลิเคชั่นนี้ประกอบด้วยมุมมองเพื่อแสดงรายการโพสต์และแบบฟอร์มง่ายๆ ในการเพิ่มโพสต์
ข้อกำหนดเบื้องต้น
- คุณควรรู้ ตอบสนอง
- คุณควรรู้แนวคิด redux และ redux-toolkit และต้องการใช้แนวคิดเหล่านี้ในโครงการของคุณ
- การมีประสบการณ์กับ TypeScript ถือเป็นข้อดี แต่คุณสามารถลบส่วน TS ออกจากโค้ดได้ และมันจะทำงานกับ JavaScript ให้คุณได้ :)
คุณสามารถค้นหาพื้นที่เก็บข้อมูลและรหัสทั้งหมดที่ฉันพูดถึงในบทความนี้ได้ "ที่นี่" สามารถดูโปรเจ็กต์สุดท้ายเวอร์ชันแสดงสดได้ "ที่นี่" ฉันยังข้ามคำอธิบายสไตล์หรือส่วน JSX ง่ายๆ เนื่องจากหัวข้อหลักที่นี่คือ rtk-query
มันทำงานอย่างไร?
อย่างที่ฉันได้กล่าวไว้ก่อนหน้านี้ rtk-query เป็นเครื่องมือดึงข้อมูลที่มีฟีเจอร์มากมายเพื่อทำให้โปรเจ็กต์ของคุณสะอาดขึ้นและนำกลับมาใช้ใหม่ได้มากขึ้น บริการของคุณสามารถสร้างในรูปแบบของปลายทางและการสืบค้นได้โดยใช้ createApi ซึ่งคล้ายกับ createReducer โดยสิ้นเชิง มันสร้าง hooks สำหรับการสืบค้นของคุณและคุณสามารถใช้มันได้อย่างง่ายดายในส่วนประกอบของคุณ คุณไม่จำเป็นต้องมีสถานะการโหลด การจัดการข้อผิดพลาด ฟังก์ชันการดึงข้อมูล การดึงข้อมูลล่วงหน้าด้วย useEffect และตัวแยกวิเคราะห์ข้อมูลอีกต่อไป เนื่องจาก rtk-query จะดูแลเรื่องนั้นให้กับคุณ
จริงๆ แล้ว การสืบค้นเป็นส่วนเล็กๆ ของบริการของคุณ ซึ่งอาจเป็น GET หรือ POST เพื่อรับหรือส่งข้อมูลไปยังเซิร์ฟเวอร์ได้ การสืบค้นทั้งหมดที่เกี่ยวข้องกับเอนทิตีจะรวมอยู่ในจุดสิ้นสุดเดียวที่สร้างโดย createApi ท้ายที่สุดแล้ว ชั้นบริการของคุณคือการรวมกันของตำแหน่งข้อมูลต่างๆ รวมถึงการสืบค้นของตัวเอง
rtk-query ใช้ fetch API จาก JavaScript แต่คุณสามารถกำหนดค่าให้ใช้ "axios" หรือ graphql ได้ นอกจากนี้ยังล้อมคำค้นหาของคุณด้วย "immer" ดังนั้นการเปลี่ยนแปลงข้อมูลจึงไม่ใช่เรื่องกังวลอีกต่อไป
การสร้างรากฐาน
คุณสามารถเลือกเครื่องมือ ใดก็ได้ และการตั้งค่าที่คุณต้องการสำหรับโปรเจ็กต์นี้ ฉันจะใช้ Vite แต่ไม่สำคัญว่าคุณจะชอบ CRA มากกว่าหรือต้องการกำหนดค่าสำเร็จรูปของคุณเอง หลังจากสร้างโปรเจ็กต์ React แล้ว เราควรติดตั้ง redux-toolkit + axios และกำหนดค่า:
npm install @reduxjs/toolkit react-redux axios
ขั้นแรก เราควรสร้างฟังก์ชันการสืบค้นพื้นฐานเพื่อกำหนดค่า rtk-query เพื่อใช้ axios:
// src/services/api.ts const axiosBaseQuery = (): BaseQueryFn<AxiosRequestConfig, unknown, AxiosError> => async ({ url, method, data, params }) => { try { Axios.defaults.baseURL = "https://jsonplaceholder.typicode.com/"; const result = await Axios({ url, method, data, params, }); return { data: result.data }; } catch (axiosError) { const error = axiosError as AxiosError; return { error, }; } };
BaseQueryFn เป็นคำมั่นสัญญาที่ควรแก้ไขคำขอของเราด้วยข้อมูลบางส่วนหรือปฏิเสธด้วยวัตถุที่มีข้อผิดพลาด ที่นี่ เราได้รับการกำหนดค่าคำขอแบบง่ายของ axios จากการสืบค้นของเรา และดำเนินการดึงข้อมูล API ด้วย axios จากนั้นเราจะส่งคืนข้อมูลหากทุกอย่างเรียบร้อยดี มิฉะนั้น เราจะปฏิเสธด้วยวัตถุที่มีข้อผิดพลาด ตรวจสอบให้แน่ใจว่าได้ส่งคืนข้อมูลหรือข้อผิดพลาดในออบเจ็กต์ ไม่เช่นนั้น rtk-query จะไม่เข้าใจ
เราจะใช้ API ฟรี JSONPlaceholder ในโปรเจ็กต์นี้
ประการที่สอง เราควรบอก rtk-query ให้ใช้ฟังก์ชันการสืบค้นแบบกำหนดเองของเราแทน JS fetch API สามารถทำได้โดยส่งฟังก์ชันของเราไปที่ createApi
// src/services/api.ts export const apiService = createApi({ baseQuery: axiosBaseQuery(), endpoints: () => ({}), });
ตอนนี้เราสามารถเพิ่มการสืบค้นของเราไปยังจุดสิ้นสุดได้ แต่อย่างที่คุณเห็น เราได้ส่งคืนวัตถุเปล่า เหตุผลก็คือในโลกแห่งความเป็นจริง เราไม่ต้องการให้บริการทั้งหมดของเราอยู่ในไฟล์ที่ยุ่งยาวเพียงไฟล์เดียว เราต้องการแยกออกเป็นโมดูลเล็กๆ หลายโมดูลเพื่อให้มีโค้ดเบสที่สะอาดยิ่งขึ้น ดังนั้นเราจึงส่งคืนออบเจ็กต์ว่างและเพียงแค่ ฉีด จุดสิ้นสุดของเราเข้าไปในออบเจ็กต์นี้ในภายหลัง
สุดท้าย เราต้องเพิ่ม apiService ไปยัง Redux Store และเพิ่ม Store ลงในแอปของเรา:
// src/store/store.ts export const store = configureStore({ reducer: { [apiService.reducerPath]: apiService.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(apiService.middleware), }); // src/main.tsx <Provider store={store}> <App /> </Provider>
ทำไม? apiService ยังมีตัวลดส่วน Redux ที่สร้างขึ้นอัตโนมัติและมิดเดิลแวร์แบบกำหนดเองที่จัดการอายุการใช้งานการสมัคร จำเป็นต้องเพิ่มทั้งสองสิ่งนี้ในร้านค้า Redux
สร้างบริการแรกของเรา
เราเริ่มต้นด้วยปลายทาง GET เพื่อดึงรายการโพสต์จากเซิร์ฟเวอร์ เราสามารถใช้ injectEndpoint API จาก rtk-query เพื่อสร้างตำแหน่งข้อมูลและเพิ่มลงในชั้นบริการพื้นฐานของเรา
// src/services/posts.ts // Interface of JSONPlaceholder posts export interface Post { id: number; userId: number; title: string; body: string; } export const postService = apiService.injectEndpoints({ endpoints: (build) => ({ // query<ResultType, QueryArg> getPosts: build.query<Post[], null>({ query: () => ({ method: "GET", url: "posts" }), }), }), }); // Auto-generated hooks export const { useGetPostsQuery } = postService;
endpoints เป็นฟังก์ชันที่มีอาร์กิวเมนต์บังคับหนึ่งอาร์กิวเมนต์ — build — ซึ่งส่งคืนออบเจ็กต์ของการสืบค้นที่แตกต่างกัน build เป็นโปรแกรมอรรถประโยชน์ที่ช่วยให้คุณสร้างจุดสิ้นสุดของคุณโดยใช้ตัวสร้างที่แตกต่างกัน build.query ควรใช้สำหรับจุดสิ้นสุด GET และ build.mutation ควรใช้กับปลายทางอื่นๆ เช่น POST และ PATCH มันคล้ายกับที่เรามีใน Graphql นิดหน่อย ตัวสร้างเหล่านี้ได้รับวัตถุที่มีหนึ่งอาร์กิวเมนต์บังคับซึ่งก็คือ query การสืบค้น หรือพูดง่ายๆ ก็คือฟังก์ชันที่คุณใช้เพื่อดึงข้อมูล API และควรส่งคืนการกำหนดค่าคำขอ axios ของคุณ เช่น วิธีการ พารามิเตอร์ และ url
จากนั้น rtk-query จะสร้าง hooks เพื่อให้คุณดึงข้อความค้นหาเหล่านี้ได้อย่างง่ายดาย คุณสามารถส่งการตั้งค่าเพิ่มเติมไปยังตัวสร้างแบบสอบถามของคุณ เช่น transformResponse ซึ่งเราไม่ครอบคลุม เนื่องจากอยู่นอกขอบเขตของบทความนี้
มาใช้บริการครั้งแรกของเรากัน
ถึงเวลาใช้บริการของเราแล้ว rtk-query สร้าง hook useGetPostsQuery ให้เรา เบ็ดนี้ส่งคืนวัตถุที่ประกอบด้วยค่าที่มีประโยชน์บางอย่างที่จะใช้ เช่นเดียวกับสถานะการโหลด ข้อผิดพลาด ข้อมูลที่ส่งคืน และฟังก์ชันการดึงข้อมูล
- สถานะการโหลด: มันจะเป็น จริง เมื่อเราดำเนินการค้นหานี้ในองค์ประกอบของเรา
- ออบเจ็กต์ข้อผิดพลาด: หากการค้นหาของเราล้มเหลว ออบเจ็กต์ข้อผิดพลาดจะถูกส่งกลับ มิฉะนั้นจะเป็น null
- ข้อมูล:ข้อมูลที่ส่งคืนของเรา null จนกว่าการสืบค้นของเราจะดำเนินการ สำเร็จ
- ฟังก์ชันการดึงข้อมูล:เราสามารถใช้ฟังก์ชันนี้เพื่อเรียกใช้แบบสอบถามอีกครั้งเมื่อใดก็ตามที่เราต้องการ เช่น ในกรณีที่เราต้องการให้มีปุ่ม “ลองอีกครั้ง” หรือปุ่มรีเฟรช
// src/components/PostList.tsx const PostList: React.FC<unknown> = () => { const { data: posts, isLoading, error, refetch } = useGetPostsQuery(null); if (isLoading) { return <div>Loading posts...</div>; } if (error) { return <ErrorBanner error={error as AxiosError} refetch={refetch} />; } return ( <div> <h2>Posts</h2> <div> {posts?.map((post) => ( <div className="postItem"> <h4>{post.title}</h4> <p>{post.body}</p> </div> ))} </div> </div> ); }; export default PostList;
ฮุค useQuery จะดึงข้อมูล API ของเราบนคอมโพเนนต์เมานท์เป็นครั้งแรก เราสามารถควบคุมมันได้ด้วยตนเองโดยใช้ useLazyQuery แทน อย่างที่คุณเห็น ส่วนประกอบของเราสะอาดขึ้นมากแล้ว และเราไม่ได้ใช้ useEffect หรือ useState ใด ๆ ในองค์ประกอบของเรา
มาสร้างฟอร์มของเรากันเถอะ
หากต้องการก้าวต่อไป เราจะกล่าวถึงคำถามเกี่ยวกับการเปลี่ยนแปลงหนึ่งรายการด้วย เราต้องการสร้างโพสต์โดยใช้แบบฟอร์มที่ได้รับชื่อและเนื้อหาจากผู้ใช้ เริ่มจากแบบสอบถามกันก่อน:
// src/services/posts.ts export interface CreatePostDto { title: string; body: string; } export const postService = apiService.injectEndpoints({ endpoints: (build) => ({ // query<ResultType, QueryArg> getPosts: build.query<Post[], null>({ query: () => ({ method: "GET", url: "posts" }), }), // We use mutation for POST endpoints createPost: build.mutation<Post, CreatePostDto>({ query: (data) => ({ method: "POST", url: "posts", // We pass static userId to simplify this part data: { userId: 1, ...data }, }), }), }), }); // Auto-generated hooks export const { useGetPostsQuery, useCreatePostMutation } = postService;
เมื่อเปรียบเทียบกับตำแหน่งข้อมูล GET เรามีความแตกต่าง 2 ประการดังนี้:
- เนื่องจากเราต้องการเพิ่มข้อมูลด้วยวิธี POST เราจึงใช้ build.mutation แทน build.query
- เราระบุอินเทอร์เฟซใหม่สำหรับอาร์กิวเมนต์ของจุดสิ้นสุดของเราและส่งข้อมูลที่เกี่ยวข้องไปยังคำขอ axios ของเรา
ตอนนี้เราควรใช้บริการใหม่นี้ในองค์ประกอบแบบฟอร์มของเรา:
// src/components/PostForm.tsx const PostForm: React.FC<unknown> = () => { const [title, setTitle] = useState<string>(""); const [body, setBody] = useState<string>(""); const [createPost, { isLoading }] = useCreatePostMutation(); const submitForm = async (e: FormEvent) => { e.preventDefault(); try { await createPost({ title, body }); } catch (e) { console.log(e); } }; return ( <form onSubmit={submitForm}> <input name="title" type="text" placeholder="Title" value={title} onChange={(e) => setTitle(e.currentTarget.value)} /> <textarea name="body" placeholder="Body..." rows={5} value={body} onChange={(e) => setBody(e.currentTarget.value)} /> <input type="submit" value={isLoading ? "Wait..." : "Submit"} disabled={isLoading} /> </form> ); }; export default PostForm;
useMutation hook ส่งกลับสิ่งอันดับ รายการแรกของอาร์เรย์นี้คือ fetch API ซึ่งคุณสามารถเรียกใช้ด้วยอาร์กิวเมนต์ที่ระบุได้ รายการที่สองคือออบเจ็กต์ที่ประกอบด้วยค่าที่สำคัญบางอย่าง เช่น สถานะการโหลด นอกจากนี้ยังส่งคืนออบเจ็กต์ข้อผิดพลาดที่เราสามารถใช้เพื่อแสดงข้อผิดพลาดในกรณีที่การส่งข้อมูลล้มเหลว
ห่อมันขึ้นมา
เราได้ครอบคลุมแนวคิดพื้นฐานที่สำคัญที่สุดของ rtk-query แล้ว เราได้เห็นแล้วว่าเราจะสร้างบริการและจุดสิ้นสุดของเราในรูปแบบโมดูลาร์ได้อย่างไร และวิธีใช้ hooks ที่สร้างขึ้นอัตโนมัติที่ยอดเยี่ยมในส่วนประกอบของเรา หลังจากนั้น ฉันจะเผยแพร่บทความติดตามผลที่อธิบายคุณลักษณะขั้นสูงและแนวคิดของ rtk-query เช่น การแคช การดึงการกำหนดค่า การแปลงข้อมูล ฯลฯ แข็งแกร่ง>
โค้ดเบสเต็มรูปแบบของทุกสิ่งที่เรากล่าวถึงได้รับการเผยแพร่ใน "พื้นที่เก็บข้อมูล" นี้ และปรับใช้บน "หน้า" นี้
หากคุณมีข้อเสนอแนะหรือข้อเสนอแนะ อย่าลังเลที่จะถามฉันทาง "อีเมลส่วนตัว" ของฉัน หรือเพิ่มความคิดเห็น และหากคุณชอบบทความนี้ อย่าลืมปรบมือ!
เนื้อหาเพิ่มเติมได้ที่ PlainEnglish.io.
ลงทะเบียนเพื่อรับ จดหมายข่าวรายสัปดาห์ฟรี ของเรา ติดตามเราบน Twitter, LinkedIn, YouTube และ Discord .
สนใจที่จะขยายขนาดการเริ่มต้นซอฟต์แวร์ของคุณหรือไม่ ลองดูที่ วงจร