
สหายทดสอบประสิทธิภาพสำหรับ React และ React Native

อ่านเอกสาร
reassure-tests.sh ).gitignorereassure-tests.sh )measureRendersMeasureRendersOptions ประเภทประเภทmeasureFunctionMeasureFunctionOptions ประเภทconfigure ค่าฟังก์ชันresetToDefaultsคุณต้องการให้แอพ React Native ของคุณทำงานได้ดีและรวดเร็วตลอดเวลา เป็นส่วนหนึ่งของเป้าหมายนี้คุณโปรไฟล์แอพสังเกตรูปแบบการแสดงผลใช้การบันทึกความทรงจำในสถานที่ที่เหมาะสม ฯลฯ แต่มันเป็นคู่มือและง่ายเกินไปที่จะแนะนำการถดถอยประสิทธิภาพที่ไม่ได้ตั้งใจซึ่งจะถูกจับในช่วง QA หรือแย่กว่านั้นโดยผู้ใช้ .
ให้ความมั่นใจกับคุณสามารถทำการทดสอบการถดถอยประสิทธิภาพของแอป Native โดยอัตโนมัติบน CI หรือเครื่องในท้องถิ่น ในทำนองเดียวกันคุณเขียนการรวมการรวมและการทดสอบหน่วยที่ตรวจสอบโดยอัตโนมัติว่าแอปของคุณยังคง ทำงานได้อย่างถูกต้อง คุณสามารถเขียนการทดสอบประสิทธิภาพที่ตรวจสอบว่าแอปของคุณยังคง ทำงานได้อย่างมีประสิทธิภาพ
คุณสามารถคิดว่ามันเป็นไลบรารีการทดสอบประสิทธิภาพการตอบสนอง ในความเป็นจริงความมั่นใจได้รับการออกแบบมาเพื่อนำกลับมาใช้ใหม่ในการทดสอบไลบรารีการทดสอบดั้งเดิมของคุณและตั้งค่าให้มากที่สุด
ให้ความมั่นใจในการทำงานโดยการวัดลักษณะการแสดงผล - ระยะเวลาและการนับ - ของสถานการณ์การทดสอบที่คุณให้และเปรียบเทียบกับเวอร์ชันที่เสถียร มันทำซ้ำสถานการณ์หลายครั้งเพื่อลดผลกระทบของการเปลี่ยนแปลงแบบสุ่มในเวลาแสดงผลที่เกิดจากสภาพแวดล้อมรันไทม์ จากนั้นใช้การวิเคราะห์ทางสถิติเพื่อตรวจสอบว่าการเปลี่ยนแปลงรหัสมีนัยสำคัญทางสถิติหรือไม่ เป็นผลให้มันสร้างรายงานที่มนุษย์อ่านได้สรุปผลลัพธ์และแสดงบน CI หรือเป็นความคิดเห็นต่อคำขอดึงของคุณ
นอกเหนือจากการวัดการเรนเดอร์ส่วนประกอบแล้วยังสามารถวัดการดำเนินการของฟังก์ชั่น JavaScript ปกติ
หากต้องการติดตั้งให้ความมั่นใจให้เรียกใช้คำสั่งต่อไปนี้ในโฟลเดอร์แอพของคุณ:
ใช้เส้นด้าย
yarn add --dev reassureใช้ NPM
npm install --save-dev reassureนอกจากนี้คุณยังจะต้องมีการตั้งค่า jest jest ที่ใช้งานได้รวมถึงหนึ่งในไลบรารีการทดสอบดั้งเดิมหรือไลบรารีการทดสอบปฏิกิริยา
ดูคู่มือการติดตั้ง
คุณสามารถตรวจสอบโครงการตัวอย่างของเรา:
ให้ความมั่นใจว่าจะพยายามตรวจสอบว่าคุณติดตั้งไลบรารีการทดสอบใด หากมีการตอบสนองของไลบรารีการทดสอบดั้งเดิมและไลบรารีการทดสอบปฏิกิริยาจะมีอยู่มันจะเตือนคุณเกี่ยวกับสิ่งนั้นและให้ความสำคัญกับการตอบสนองของไลบรารีการทดสอบดั้งเดิม คุณสามารถระบุไลบรารีการทดสอบที่จะใช้อย่างชัดเจนโดยใช้ตัวเลือก configure :
configure ( { testingLibrary : 'react-native' } ) ;
// or
configure ( { testingLibrary : 'react' } ) ;คุณควรตั้งค่าไว้ในไฟล์การตั้งค่า jest ของคุณและคุณสามารถแทนที่ไฟล์ทดสอบโดยเฉพาะหากจำเป็น
ตอนนี้มีการติดตั้งไลบรารีแล้วคุณสามารถเขียนสถานการณ์การ .perf-test.tsx ครั้งแรกของคุณในไฟล์ที่ .perf-test.js
// ComponentUnderTest.perf-test.tsx
import { measureRenders } from 'reassure' ;
import { ComponentUnderTest } from './ComponentUnderTest' ;
test ( 'Simple test' , async ( ) => {
await measureRenders ( < ComponentUnderTest / >);
} ) ; การทดสอบนี้จะวัดเวลาเรนเดอร์ของ ComponentUnderTest ระหว่างการติดตั้งและผลการซิงค์ที่เกิดขึ้น
หมายเหตุ : ให้ความมั่นใจว่าจะจับคู่ชื่อไฟล์ทดสอบโดยอัตโนมัติโดยใช้ตัวเลือก jest
--testMatchกับค่า"**/__perf__/**/*.[jt]s?(x)", "**/*.(perf|perf-test).[jt]s?(x)"อย่างไรก็ตามหากคุณต้องการผ่านตัวเลือกที่กำหนดเอง--testMatchหรือ--testRegexคุณสามารถเพิ่มลงในสคริปต์reassure measureเพื่อส่งผ่านลูกโลกของคุณเอง เพิ่มเติมเกี่ยวกับ--testMatchและ--testRegexในเอกสารตลก
หากส่วนประกอบของคุณมีตรรกะ async หรือคุณต้องการทดสอบการโต้ตอบบางอย่างคุณควรผ่านตัวเลือก scenario :
import { measureRenders } from 'reassure' ;
import { screen , fireEvent } from '@testing-library/react-native' ;
import { ComponentUnderTest } from './ComponentUnderTest' ;
test ( 'Test with scenario' , async ( ) => {
const scenario = async ( ) => {
fireEvent . press ( screen . getByText ( 'Go' ) ) ;
await screen . findByText ( 'Done' ) ;
} ;
await measureRenders ( < ComponentUnderTest / >, { scenario });
} ) ; ร่างกายของฟังก์ชั่น scenario กำลังใช้วิธีการทดสอบรีแอกเนทีฟพื้นเมืองที่คุ้นเคย
ในกรณีของการใช้ไลบรารีการทดสอบ React Native Library ต่ำกว่า V10.1.0 ซึ่งไม่มีตัวช่วย screen ฟังก์ชั่น scenario แสดงเป็นอาร์กิวเมนต์แรก:
import { measureRenders } from 'reassure' ;
import { fireEvent } from '@testing-library/react-native' ;
test ( 'Test with scenario' , async ( ) => {
const scenario = async ( screen ) => {
fireEvent . press ( screen . getByText ( 'Go' ) ) ;
await screen . findByText ( 'Done' ) ;
} ;
await measureRenders ( < ComponentUnderTest / >, { scenario });
} ) ; หากการทดสอบของคุณมีการเปลี่ยนแปลงแบบ async ใด ๆ คุณจะต้องตรวจสอบให้แน่ใจว่าสถานการณ์รอการเปลี่ยนแปลงเหล่านี้เพื่อชำระเช่นโดยใช้การค้นหา findBy waitFor หรือรอฟังก์ชั่น waitForElementToBeRemoved จาก RNTL
ในการวัดประสิทธิภาพการทดสอบครั้งแรกของคุณคุณต้องเรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัล:
yarn reassure คำสั่งนี้จะเรียกใช้การทดสอบของคุณหลายครั้งโดยใช้ JEST รวบรวมสถิติประสิทธิภาพและจะเขียนลงในไฟล์ .reassure/current.perf หากต้องการตรวจสอบการตั้งค่าของคุณให้ตรวจสอบว่าไฟล์เอาต์พุตมีอยู่หลังจากเรียกใช้คำสั่งเป็นครั้งแรกหรือไม่
หมายเหตุ: คุณสามารถเพิ่ม
.reassure/โฟลเดอร์ลงในไฟล์.gitignoreของคุณเพื่อหลีกเลี่ยงการกระทำของคุณโดยไม่ตั้งใจ
ให้ความมั่นใจกับ CLI จะพยายามตรวจจับชื่อสาขาซอร์สโค้ดของคุณโดยอัตโนมัติและส่งแฮชเมื่อคุณใช้ Git คุณสามารถแทนที่ตัวเลือกเหล่านี้เช่นหากคุณใช้ระบบควบคุมเวอร์ชันอื่น:
yarn reassure --branch [branch name] --commit-hash [commit hash] ในการตรวจจับการเปลี่ยนแปลงประสิทธิภาพคุณต้องวัดประสิทธิภาพของรหัสสองเวอร์ชันปัจจุบันของคุณ (รหัสที่แก้ไขแล้ว) และพื้นฐาน (จุดอ้างอิงของคุณเช่นสาขา main ) ในการวัดประสิทธิภาพในสองสาขาคุณต้องสลับสาขาใน Git หรือโคลนสองสำนักที่เก็บของคุณ
เราต้องการให้งานนี้ทำงานบน CI โดยอัตโนมัติ ในการทำเช่นนั้นคุณจะต้องสร้างสคริปต์การทดสอบประสิทธิภาพ คุณควรบันทึกไว้ในที่เก็บของของคุณเช่น reassure-tests.sh
สคริปต์เวอร์ชันง่าย ๆ โดยใช้วิธีการเปลี่ยนแปลงสาขามีดังนี้:
#! /usr/bin/env bash
set -e
BASELINE_BRANCH= ${GITHUB_BASE_REF := " main " }
# Required for `git switch` on CI
git fetch origin
# Gather baseline perf measurements
git switch " $BASELINE_BRANCH "
yarn install
yarn reassure --baseline
# Gather current perf measurements & compare results
git switch --detach -
yarn install
yarn reassureเพื่อให้การตั้งค่าการรวม CI และสิ่งที่จำเป็นต้องมีทั้งหมดสะดวกยิ่งขึ้นเราได้เตรียมคำสั่ง CLI เพื่อสร้างเทมเพลตที่จำเป็นทั้งหมดเพื่อให้คุณเริ่มต้นด้วย
เพียงแค่วิ่ง:
yarn reassure initสิ่งนี้จะสร้างโครงสร้างไฟล์ต่อไปนี้
├── <ROOT>
│ ├── reassure-tests.sh
│ ├── dangerfile.ts/js (or dangerfile.reassure.ts/js if dangerfile.ts/js already present)
│ └── .gitignore
reassure-tests.sh )สคริปต์พื้นฐานช่วยให้คุณสามารถให้ความมั่นใจกับ CI เพิ่มเติมเกี่ยวกับความสำคัญและโครงสร้างของไฟล์นี้ในส่วนต่อไปนี้
หากโครงการของคุณมี dangerfile.ts/js อยู่แล้ว CLI จะไม่แทนที่ในทางใดทางหนึ่ง แต่จะสร้างไฟล์ dangerfile.reassure.ts/js ซึ่งจะช่วยให้คุณเปรียบเทียบและอัปเดตของคุณเองตามความสะดวกของคุณ
.gitignore หากไฟล์ .gitignore มีอยู่และไม่มีการกล่าวถึง reassure ปรากฏขึ้นสคริปต์จะผนวก .reassure/ ไดเรกทอรีจนถึงที่สุด
reassure-tests.sh ) ในการตรวจจับการเปลี่ยนแปลงประสิทธิภาพคุณต้องวัดประสิทธิภาพของรหัสสองเวอร์ชันของรหัสปัจจุบัน (รหัสที่คุณแก้ไข) และพื้นฐาน (จุดอ้างอิงของคุณเช่นสาขา main ) ในการวัดประสิทธิภาพในสองสาขาคุณต้องสลับสาขาใน Git หรือโคลนสองสำนักที่เก็บของคุณ
เราต้องการให้งานนี้ทำงานบน CI โดยอัตโนมัติ ในการทำเช่นนั้นคุณจะต้องสร้างสคริปต์การทดสอบประสิทธิภาพ คุณควรบันทึกไว้ในที่เก็บของของคุณเช่น reassure-tests.sh
สคริปต์เวอร์ชันง่าย ๆ โดยใช้วิธีการเปลี่ยนแปลงสาขามีดังนี้:
#! /usr/bin/env bash
set -e
BASELINE_BRANCH= ${GITHUB_BASE_REF := " main " }
# Required for `git switch` on CI
git fetch origin
# Gather baseline perf measurements
git switch " $BASELINE_BRANCH "
yarn install
yarn reassure --baseline
# Gather current perf measurements & compare results
git switch --detach -
yarn install
yarn reassureเป็นขั้นตอนการตั้งค่าสุดท้ายคุณต้องกำหนดค่า CI ของคุณเพื่อเรียกใช้สคริปต์การทดสอบประสิทธิภาพและส่งออกผลลัพธ์ สำหรับการนำเสนอผลลัพธ์ในขณะนี้เรารวมเข้ากับ Danger JS ซึ่งรองรับเครื่องมือ CI ที่สำคัญทั้งหมด
คุณจะต้องมีการตั้งค่า JS ที่เป็นอันตรายต่อการทำงาน
จากนั้นเพิ่มความมั่นใจในอันตราย JS ปลั๊กอินให้กับ DangerFile ของคุณ:
// /<project_root>/dangerfile.reassure.ts (generated by the init script)
import path from 'path' ;
import { dangerReassure } from 'reassure' ;
dangerReassure ( {
inputFilePath : path . join ( __dirname , '.reassure/output.md' ) ,
} ) ; หากคุณยังไม่มี Dangerfile ( dangerfile.js หรือ dangerfile.ts ) คุณสามารถใช้หนึ่งที่สร้างโดยสคริปต์ reassure init โดยไม่ต้องทำการเปลี่ยนแปลงเพิ่มเติมใด ๆ
นอกจากนี้ยังอยู่ในตัวอย่างไฟล์ DangerFile ของเรา
สุดท้ายเรียกใช้ทั้งสคริปต์การทดสอบประสิทธิภาพและอันตรายในการกำหนดค่า CI ของคุณ:
- name : Run performance testing script
run : ./reassure-tests.sh
- name : Run Danger.js
run : yarn danger ci
env :
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}คุณยังสามารถตรวจสอบตัวอย่างเวิร์กโฟลว์ GitHub ของเรา
ตัวอย่างข้างต้นขึ้นอยู่กับการกระทำของ GitHub แต่ควรจะคล้ายกับไฟล์ ci -config อื่น ๆ และควรใช้เป็นข้อมูลอ้างอิงในกรณีดังกล่าวเท่านั้น
หมายเหตุ : การทดสอบประสิทธิภาพของคุณจะทำงานได้นานกว่าการทดสอบการรวมปกติ เป็นเพราะเราเรียกใช้แต่ละสถานการณ์การทดสอบหลายครั้ง (โดยค่าเริ่มต้น 10) และทำซ้ำสำหรับสองสาขาของรหัสของคุณ ดังนั้นการทดสอบแต่ละครั้งจะเรียกใช้ 20 ครั้งโดยค่าเริ่มต้น นั่นเว้นแต่คุณจะเพิ่มจำนวนนั้นให้สูงขึ้น
เราวัดเวลาเรนเดอร์ส่วนประกอบของปฏิกิริยาด้วยความแม่นยำของไมโครวินาทีในระหว่างการวัดประสิทธิภาพโดยใช้ React.Profiler ซึ่งหมายความว่ารหัสเดียวกันจะทำงานได้เร็วขึ้นหรือช้าลงขึ้นอยู่กับเครื่อง ด้วยเหตุผลนี้การวัดพื้นฐานและปัจจุบันจำเป็นต้องทำงานบนเครื่องเดียวกัน อย่างดีที่สุดพวกเขาควรจะทำงานต่อไป
ยิ่งไปกว่านั้นตัวแทน CI ของคุณต้องมีประสิทธิภาพที่มั่นคงเพื่อให้ได้ผลลัพธ์ที่มีความหมาย ไม่สำคัญว่าตัวแทนของคุณจะเร็วหรือช้าตราบใดที่มันสอดคล้องกับประสิทธิภาพ นั่นเป็นเหตุผลที่ตัวแทนไม่ควรใช้ในระหว่างการทดสอบประสิทธิภาพสำหรับงานอื่น ๆ ที่อาจส่งผลกระทบต่อการวัดเวลาในการแสดงผล
เพื่อช่วยให้คุณประเมินความมั่นคงของเครื่องของคุณคุณสามารถใช้คำสั่ง reassure check-stability มันเรียกใช้การวัดประสิทธิภาพสองครั้งสำหรับรหัสปัจจุบันดังนั้นการวัดพื้นฐานและปัจจุบันอ้างถึงรหัสเดียวกัน ในกรณีเช่นนี้การเปลี่ยนแปลงที่คาดหวังคือ 0% (ไม่มีการเปลี่ยนแปลง) ระดับของการเปลี่ยนแปลงประสิทธิภาพแบบสุ่มจะสะท้อนถึงความเสถียรของเครื่องของคุณ คำสั่งนี้สามารถรันได้ทั้งใน CI และเครื่องจักรท้องถิ่น
โดยปกติการเปลี่ยนแปลงแบบสุ่มควรต่ำกว่า 5% ผลลัพธ์ 10% และอื่น ๆ ถือว่าสูงเกินไปซึ่งหมายความว่าคุณควรทำงานเพื่อปรับความเสถียรของเครื่อง
หมายเหตุ : เป็นเคล็ดลับของทางเลือกสุดท้ายคุณสามารถเพิ่มตัวเลือก
runจากค่าเริ่มต้นที่ 10 ถึง 20, 50 หรือ 100 สำหรับการทดสอบทั้งหมดหรือบางส่วนโดยขึ้นอยู่กับสมมติฐานที่ว่าการทดสอบเพิ่มเติมจะทำให้เกิดความผันผวนของการวัด อย่างไรก็ตามจะทำให้การทดสอบของคุณทำงานได้นานขึ้น
คุณสามารถอ้างถึงตัวอย่างเวิร์กโฟลว์ GitHub ของเรา

เมื่อดูตัวอย่างคุณสามารถสังเกตได้ว่าสถานการณ์การทดสอบสามารถกำหนดให้กับหมวดหมู่บางประเภท:
measureRenders wrapper แบบกำหนดเองสำหรับฟังก์ชั่น RNTL render ที่รับผิดชอบในการแสดงผลหน้าจอที่ผ่านภายใน React.Profiler ส่วนประกอบการวัดประสิทธิภาพและการเขียนผลลัพธ์ไปยังไฟล์เอาต์พุต คุณสามารถใช้วัตถุ options ที่เป็นตัวเลือกที่อนุญาตให้ปรับแต่งด้านการทดสอบ
async function measureRenders (
ui : React . ReactElement ,
options ?: MeasureRendersOptions ,
) : Promise < MeasureResults > { MeasureRendersOptions ประเภทประเภท interface MeasureRendersOptions {
runs ?: number ;
warmupRuns ?: number ;
wrapper ?: React . ComponentType < { children : ReactElement } > ;
scenario ?: ( view ?: RenderResult ) => Promise < any > ;
writeFile ?: boolean ;
}runs : จำนวนการรันต่อซีรีส์สำหรับการทดสอบเฉพาะwarmupRuns : จำนวนการทำงานอุ่นเครื่องเพิ่มเติมที่จะทำและทิ้งก่อนการรันจริง (ค่าเริ่มต้น 1)wrapper : ส่วนประกอบตอบสนองเช่น Provider ซึ่ง ui จะถูกห่อด้วย หมายเหตุ: ระยะเวลาการเรนเดอร์ของ wrapper นั้นไม่รวมอยู่ในผลลัพธ์ มีการวัดส่วนประกอบที่ห่อหุ้มเท่านั้นscenario : ฟังก์ชั่น async แบบกำหนดเองซึ่งกำหนดการโต้ตอบของผู้ใช้ภายใน UI โดยใช้ฟังก์ชัน RNTL หรือ RTLwriteFile : (ค่าเริ่มต้น true ) ควรเขียนเอาต์พุตไปยังไฟล์ measureFunction ช่วยให้คุณสามารถห่อฟังก์ชั่นซิงโครนัสใด ๆ วัดเวลาการดำเนินการและเขียนผลลัพธ์ลงในไฟล์เอาต์พุต คุณสามารถใช้ options เพิ่มเติมเพื่อปรับแต่งแง่มุมของการทดสอบ หมายเหตุ: จำนวนการดำเนินการจะเป็นหนึ่งเสมอ
async function measureFunction (
fn : ( ) => void ,
options ?: MeasureFunctionOptions
) : Promise < MeasureResults > { MeasureFunctionOptions ประเภท interface MeasureFunctionOptions {
runs ?: number ;
warmupRuns ?: number ;
}runs : จำนวนการรันต่อซีรีส์สำหรับการทดสอบเฉพาะwarmupRuns : จำนวนการอุ่นเครื่องเพิ่มเติมที่จะทำและทิ้งก่อนที่จะวิ่งจริง การกำหนดค่าเริ่มต้นซึ่งจะใช้โดยสคริปต์การวัด วัตถุการกำหนดค่านี้สามารถแทนที่ด้วยการใช้ฟังก์ชัน configure
type Config = {
runs ?: number ;
warmupRuns ?: number ;
outputFile ?: string ;
verbose ?: boolean ;
testingLibrary ?:
| 'react-native'
| 'react'
| { render : ( component : React . ReactElement < any > ) => any ; cleanup : ( ) => any } ;
} ; const defaultConfig : Config = {
runs : 10 ,
warmupRuns : 1 ,
outputFile : '.reassure/current.perf' ,
verbose : false ,
testingLibrary : undefined , // Will try auto-detect first RNTL, then RTL
} ; runs : จำนวนการรันซ้ำในชุดต่อการทดสอบ (ช่วยให้มีความแม่นยำสูงขึ้นโดยการรวมข้อมูลเพิ่มเติม) ควรได้รับการจัดการด้วยความระมัดระวัง
warmupRuns : จำนวนการอุ่นเครื่องเพิ่มเติมที่จะทำและทิ้งก่อนที่จะวิ่งจริง outputFile : ชื่อของไฟล์บันทึกจะถูกบันทึกลงใน verbose : สร้างความมั่นใจในบันทึกมากขึ้นเช่นสำหรับวัตถุประสงค์ในการแก้ไขจุดประสงค์ testingLibrary : สถานที่ที่จะมองหาฟังก์ render น 'react-native' 'react' cleanup ฟังก์ชั่น render และ cleanup configure ค่าฟังก์ชัน function configure ( customConfig : Partial < Config > ) : void ; ฟังก์ชั่น configure สามารถแทนที่พารามิเตอร์การกำหนดค่าเริ่มต้น
resetToDefaults resetToDefaults ( ) : void รีเซ็ตการกำหนดค่าปัจจุบันเป็นวัตถุ defaultConfig ดั้งเดิม
คุณสามารถใช้ตัวแปรสภาพแวดล้อมที่มีอยู่เพื่อเปลี่ยนการตั้งค่านักวิ่งทดสอบของคุณ
TEST_RUNNER_PATH : เส้นทางอื่นสำหรับนักวิ่งทดสอบของคุณ ค่าเริ่มต้นเป็น 'node_modules/.bin/jest' หรือบน windows 'node_modules/jest/bin/jest'TEST_RUNNER_ARGS : ชุดของอาร์กิวเมนต์ที่ป้อนให้กับนักวิ่ง ค่าเริ่มต้นเป็น '--runInBand --testMatch "**/__perf__/**/*.[jt]s?(x)", "**/*.(perf|perf-test).[jt]s?(x)"'ตัวอย่าง:
TEST_RUNNER_PATH=myOwnPath/jest/bin yarn reassureดูคู่มือการสนับสนุนเพื่อเรียนรู้วิธีการมีส่วนร่วมในที่เก็บและเวิร์กโฟลว์การพัฒนา
มิกซ์
ให้ความมั่นใจเป็นโครงการโอเพ่นซอร์สและจะยังคงใช้งานได้ฟรีเสมอ โครงการได้รับการพัฒนาในการเป็นหุ้นส่วนอย่างใกล้ชิดกับ Entain และเดิมเป็นโครงการภายใน บริษัท ต้องขอบคุณความตั้งใจของพวกเขาที่จะพัฒนาระบบนิเวศของ React & React Native เราตัดสินใจที่จะทำให้เป็นโอเพนซอร์ส ถ้าคุณคิดว่ามันเจ๋งโปรดแสดงให้เห็นได้ไหม
CallStack เป็นกลุ่มของ React และ React Native Experts หากคุณต้องการความช่วยเหลือเกี่ยวกับสิ่งเหล่านี้หรือต้องการทักทายโปรดติดต่อเราที่ [email protected]!
ชอบโครงการ? ⚛เข้าร่วมทีม CallStack ที่ทำสิ่งที่น่าทึ่งสำหรับลูกค้าและไดรฟ์ตอบสนองโอเพนซอร์สพื้นเมือง!