باستخدام Typescript مع React - دليل المبتدئين

الأصدقاء ، عشية نهاية الأسبوع ، نريد أن نطلعكم على منشور آخر مثير للاهتمام نريد أن نتزامن مع إطلاق مجموعة جديدة في الدورة التدريبية "JavaScript Developer" .



بعد أن أمضيت الأشهر القليلة الماضية في تطوير تطبيقات React ومكتبات باستخدام Typescript ، قررت مشاركة بعض الأشياء التي تعلمتها خلال ذلك الوقت. في هذا الدليل ، سوف أخبرك عن القوالب التي أستخدمها لـ Typescript و React في 80٪ من الحالات.

هل يجب أن أتعلم Typescript من أجل تطوير تطبيق React؟ يستحق ، لا يزال يستحق كل هذا العناء! بالنسبة لي ، أدركت في الممارسة العملية أن الكتابة الصارمة تؤدي إلى كتابة كود أكثر موثوقية ، وتطور سريع ، خاصة في المشروعات الكبيرة. في البداية ، من المحتمل أن تصاب بخيبة أمل ، ولكن أثناء عملك ، ستجد أن القالب الأدنى على الأقل سيكون مفيدًا للغاية.

وإذا علقت بشيء ما ، تذكر أنه يمكنك دائمًا كتابة شيء مثل أي شيء. أي هو صديقك الجديد. والآن سننتقل مباشرة إلى الأمثلة.

رد فعل المكون الأساسي الخاص بك مع typescript


كيف يبدو رد فعل المكون القياسي على الآلة الكاتبة؟ دعونا نقارن ذلك بمكون التفاعل في جافا سكريبت.

import React from 'react' import PropTypes from 'prop-types' export function StandardComponent({ children, title = 'Dr.' }) { return ( <div> {title}: {children} </div> ) } StandardComponent.propTypes = { title: PropTypes.string, children: PropTypes.node.isRequired, } 

والآن النسخة المطبوعة:

 import * as React from 'react' export interface StandardComponentProps { title?: string children: React.ReactNode } export function StandardComponent({ children, title = 'Dr.', }: StandardComponentProps) { return ( <div> {title}: {children} </div> ) } 

مشابهة جدا ، أليس كذلك؟ لقد propTypes بواجهة typescript .

يبقى رأس prop اختياريًا ، في حين أن دعامة الوريث لا تزال مطلوبة. قمنا بتصدير واجهتنا في حالة احتياج مكون آخر إلى رابط إليها.

توسيع سمات HTML القياسية


إذا كنا نريد أن يكون المكون الرئيسي قادرًا على توفير سمات div مكتوبة إضافية ، مثل aria-hidden أو style أو className ، فيمكننا تعريفها في interface أو توسيع interface المدمجة. في المثال أدناه ، نقول أن مكوننا يقبل أي خصائص div قياسية بالإضافة إلى الرأس والأحفاد.

 import * as React from 'react' export interface SpreadingExampleProps extends React.HTMLAttributes<HTMLDivElement> { title?: string children: React.ReactNode } export function SpreadingExample({ children, title = 'Dr.', ...other }: SpreadingExampleProps) { return ( <div {...other}> {title}: {children} </div> ) } 

التعامل مع الحدث


يمكننا كتابة معالجات الأحداث للتأكد من أن وسيطة الحدث من النوع الصحيح. يوضح المثال التالي طرقًا مختلفة لتحقيق هذا الهدف:

 export interface EventHandlerProps { onClick: (e: React.MouseEvent) => void } export function EventHandler({ onClick }: EventHandlerProps) { // handle focus events in a separate function function onFocus(e: React.FocusEvent) { console.log('Focused!', e.currentTarget) } return ( <button onClick={onClick} onFocus={onFocus} onKeyDown={e => { // When using an inline function, the appropriate argument signature // is provided for us }} > Click me! </button> ) } 

لست متأكدا أي توقيع حجة لاستخدام؟ في المحرر ، مرّر الماوس فوق الخاصية المقابلة لمعالج الأحداث.

باستخدام الأدوية مع مكونات التفاعل


هذه ميزة أكثر تقدمًا ، لكنها قوية حقًا. عادة ، يمكنك تحديد أنواع البيانات في مكونات التفاعل مع سمات محددة. افترض أن المكون الخاص بك يحتاج إلى كائن profile .

 interface ProfileType { name: string image: string age: number | null } interface ProfilesProps { profiles: Array<ProfileType> } function Profiles(props: ProfilesProps) { // render a set of profiles } 

الآن دعنا نتخيل أن لديك مكونًا يمكنه قبول مجموعة من أي نوع. الأدوية الجنيسة مثل الطرود البريدية. لا يحتاج حامل البريد (المكون الخاص بنا) إلى معرفة محتويات الحزمة التي ترسلها ، لكن المرسل (المكون الأصلي) يتوقع أن يتلقى المستلم المحتوى الذي أرسله.

نطبقها مثل هذا:

 interface GenericsExampleProps<T> { children: (item: T) => React.ReactNode items: Array<T> } export function GenericsExample<T>({ items, children, }: GenericsExampleProps<T>) { return ( <div> {items.map(item => { return children(item) })} </div> ) } 

مثال غريب بعض الشيء ... ومع ذلك ، فإنه يوضح الجوهر. يقبل المكون مجموعة من العناصر من أي نوع ، ويمر من خلالها ويستدعي وظيفة children كدالة تجسيد مع عنصر صفيف. عندما يوفر المكون الرئيسي الخاص بنا برنامج تقديم كعارض ، سيتم كتابة العنصر بشكل صحيح!

لا أفهم؟ هذا طبيعي. أنا شخصياً لم أحصل على الأدوية الجنيسة حتى النهاية ، لكن من غير المحتمل أن تحتاج إلى فهمها تمامًا. ومع ذلك ، كلما كنت تعمل باستخدام typescript ، كلما كان ذلك منطقيًا.

السنانير الكتابة


السنانير تعمل في الغالب من خارج منطقة الجزاء. يمكن أن يكون الاستثناءان فقط useRef و useReducer . يوضح المثال التالي كيف يمكننا كتابة المراجع.

 import * as React from 'react' interface HooksExampleProps {} export function HooksExample(props: HooksExampleProps) { const [count, setCount] = React.useState(0) const ref = React.useRef<HTMLDivElement | null>(null) // start our timer React.useEffect( () => { const timer = setInterval(() => { setCount(count + 1) }, 1000) return () => clearTimeout(timer) }, [count] ) // measure our element React.useEffect( () => { if (ref.current) { console.log(ref.current.getBoundingClientRect()) } }, [ref] ) return <div ref={ref}>Count: {count}</div> } 

تتم كتابة حالتنا تلقائيًا ، لكننا كتبنا المرجع يدويًا للإشارة إلى أنها ستكون null أو تحتوي على عنصر div . عندما نصل إلى المرجع في وظيفة useEffect ، نحتاج إلى التأكد من أنها ليست null .

الكتابة علبة التروس


مع علبة التروس هو أكثر تعقيدا قليلا ، ولكن إذا تم كتابتها بشكل صحيح ، فهذا أمر عظيم.

 // Yeah, I don't understand this either. But it gives us nice typing // for our reducer actions. type Action<K, V = void> = V extends void ? { type: K } : { type: K } & V // our search response type interface Response { id: number title: string } // reducer actions. These are what you'll "dispatch" export type ActionType = | Action<'QUERY', { value: string }> | Action<'SEARCH', { value: Array<Response> }> // the form that our reducer state takes interface StateType { searchResponse: Array<Response> query: string } // our default state const initialState: StateType = { searchResponse: [], query: '', } // the actual reducer function reducer(state: StateType, action: ActionType) { switch (action.type) { case 'QUERY': return { ...state, query: action.value, } case 'SEARCH': return { ...state, searchResponse: action.value, } } } interface ReducerExampleProps { query: string } export function ReducerExample({ query }: ReducerExampleProps) { const [state, dispatch] = React.useReducer(reducer, initialState) React.useEffect( () => { if (query) { // emulate async query setTimeout(() => { dispatch({ type: 'SEARCH', value: [{ id: 1, title: 'Hello world' }], }) }, 1000) } }, [query] ) return state.searchResponse.map(response => ( <div key={response.id}>{response.title}</div> )) } 

باستخدام typeof و keyof خيارات المكونات


افترض أننا بحاجة إلى زر يمكن أن يكون له مظهر مختلف ، يتم تعريف كل منها في كائن به مجموعة من المفاتيح والأنماط ، على سبيل المثال:

 const styles = { primary: { color: 'blue', }, danger: { color: 'red', }, } 

يجب أن يقبل مكون الزر الخاص بنا خاصية type ، والتي يمكن أن تكون
أي مفتاح من كائن styles (على سبيل المثال ، "أساسي" أو "خطر" ). يمكننا كتابته بكل بساطة:

 const styles = { primary: { color: 'blue', }, danger: { color: 'red', }, } // creates a reusable type from the styles object type StylesType = typeof styles // ButtonType = any key in styles export type ButtonType = keyof StylesType interface ButtonProps { type: ButtonType } export function Button({ type = 'primary' }: ButtonProps) { return <button style={styles[type]}>My styled button</button> } 

هذه الأمثلة سوف تساعدك على الذهاب 80 ٪ من الطريق. إذا واجهتك مشكلة ، فغالبًا ما يكون الأمر يستحق
مجرد إلقاء نظرة على أمثلة مفتوحة المصدر الحالية.

Sancho UI عبارة عن مجموعة من مكونات التفاعل ،
بنيت باستخدام typescript والعاطفة.
مخطط هو مجموعة أخرى من المكونات
react مبنية على typescript .

حسنًا ، وفقًا للتقاليد المعمول بها ، نحن في انتظار تعليقاتكم.

Source: https://habr.com/ru/post/ar456124/


All Articles