logo by @sawaratsuki1004
React
v19.2
تعلم
مرجع
المجتمع
المدونة

هل هذه الصفحة مفيدة؟

في هذه الصفحة

  • Overview
  • التثبيت
  • إضافة TypeScript إلى مشروع React موجود
  • TypeScript مع مكونات React
  • أمثلة Hooks
  • useState
  • useReducer
  • useContext
  • useMemo
  • useCallback
  • أنواع مفيدة
  • أحداث DOM
  • Children
  • Style Props
  • Further learning

    البدأ

  • بداية سريعة
    • شرح تطبيقي: لعبة تيك تاك تو
    • التفكير على طريقة React
  • التثبيت
    • إنشاء تطبيق React
    • بناء تطبيق React من الصفر
    • إضافة React إلى مشروع موجود بالفعل
  • الإعداد
    • تجهيز المحرر
    • استخدام TypeScript
    • أدوات مطوري React
  • React Compiler
    • مقدمة
    • التثبيت
    • التبني التدريجي
    • تصحيح الأخطاء واستكشاف المشاكل
  • تعلم React

  • وصف واجهة المستخدم (UI)
    • مكونك الأول (Component)
    • استيراد وتصدير المكونات (Components)
    • كتابة ترميز البناء بـ JSX
    • JavaScript في JSX باستخدام الأقواس المنحنية
    • تمرير الخصائص (Props) إلى مكون
    • التصيير الشرطي (Conditional Rendering)
    • تصيير القوائم (Rendering Lists)
    • الحفاظ على نقاء المكونات (Pure Components)
    • واجهتك المستخدم كشجرة (UI Tree)
  • إضافة التفاعلية (Interactivity)
    • الاستجابة للأحداث (Events)
    • الحالة (State): ذاكرة المُكَوِّن
    • التصيير والالتزام (Render and Commit)
    • الحالة (State) كلقطة
    • إضافة سلسلة من تحديثات الحالة (State) إلى قائمة انتظار
    • تحديث الكائنات (Objects) في الحالة
    • تحديث المصفوفات (Arrays) في الحالة
  • إدارة State
    • التفاعل مع Input باستخدام State
    • اختيار بنية State
    • مشاركة State بين Components
    • الحفاظ على State وإعادة ضبطها
    • استخراج منطق State إلى Reducer
    • تمرير البيانات بشكل عميق باستخدام Context
    • التوسع باستخدام Reducer و Context
  • مخارج الطوارئ (Escape Hatches)
    • الإشارة إلى القيم باستخدام Refs
    • التلاعب بـ DOM باستخدام Refs
    • التزامن مع Effects
    • قد لا تحتاج إلى Effect
    • دورة حياة Reactive Effects
    • فصل Events عن Effects
    • إزالة اعتماديات Effect
    • إعادة استخدام المنطق باستخدام Custom Hooks
تعلم React
الإعداد

استخدام TypeScript

TypeScript هي طريقة شائعة لإضافة تعريفات الأنواع إلى قواعد أكواد JavaScript. بشكل افتراضي، يدعم TypeScript JSX ويمكنك الحصول على دعم كامل لـ React Web من خلال إضافة @types/react و @types/react-dom إلى مشروعك.

You will learn

  • TypeScript مع مكونات React
  • أمثلة على الكتابة مع Hooks
  • أنواع شائعة من @types/react
  • مواقع التعلم الإضافية

التثبيت

جميع أطر عمل React الجاهزة للإنتاج توفر دعمًا لاستخدام TypeScript. اتبع الدليل الخاص بإطار العمل للتثبيت:

  • Next.js
  • Remix
  • Gatsby
  • Expo

إضافة TypeScript إلى مشروع React موجود

لتثبيت أحدث إصدار من تعريفات أنواع React:

Terminal
npm install @types/react @types/react-dom

يجب تعيين خيارات المترجم التالية في ملف tsconfig.json:

  1. يجب تضمين dom في lib (ملاحظة: إذا لم يتم تحديد خيار lib، فسيتم تضمين dom بشكل افتراضي).
  2. يجب تعيين jsx إلى أحد الخيارات الصالحة. preserve يجب أن يكون كافيًا لمعظم التطبيقات. إذا كنت تنشر مكتبة، راجع وثائق jsx لمعرفة القيمة التي يجب اختيارها.

TypeScript مع مكونات React

ملاحظة

يجب أن يستخدم كل ملف يحتوي على JSX امتداد الملف .tsx. هذا امتداد خاص بـ TypeScript يخبر TypeScript بأن هذا الملف يحتوي على JSX.

كتابة TypeScript مع React مشابهة جدًا لكتابة JavaScript مع React. الاختلاف الرئيسي عند العمل مع مكون هو أنه يمكنك توفير أنواع لـ props المكون. يمكن استخدام هذه الأنواع للتحقق من الصحة وتوفير التوثيق المضمن في المحررات.

مستعيرين مكون MyButton من دليل البداية السريعة، يمكننا إضافة نوع يصف title للزر:

function MyButton({ title }: { title: string }) { return ( <button>{title}</button> ); } export default function MyApp() { return ( <div> <h1>Welcome to my app</h1> <MyButton title="I'm a button" /> </div> ); }

ملاحظة

يمكن لهذه البيئات التجريبية معالجة كود TypeScript، لكنها لا تشغل فاحص الأنواع. هذا يعني أنه يمكنك تعديل البيئات التجريبية لـ TypeScript للتعلم، لكنك لن تحصل على أخطاء أو تحذيرات الأنواع. للحصول على فحص الأنواع، يمكنك استخدام TypeScript Playground أو استخدام بيئة تجريبية أكثر اكتمالاً عبر الإنترنت.

هذا البناء المضمن هو أبسط طريقة لتوفير الأنواع لمكون، على الرغم من أنه بمجرد أن يكون لديك بعض الحقول لوصفها، يمكن أن يصبح غير عملي. بدلاً من ذلك، يمكنك استخدام interface أو type لوصف props المكون:

interface MyButtonProps { /** The text to display inside the button */ title: string; /** Whether the button can be interacted with */ disabled: boolean; } function MyButton({ title, disabled }: MyButtonProps) { return ( <button disabled={disabled}>{title}</button> ); } export default function MyApp() { return ( <div> <h1>Welcome to my app</h1> <MyButton title="I'm a disabled button" disabled={true}/> </div> ); }

النوع الذي يصف props مكونك يمكن أن يكون بسيطًا أو معقدًا حسب حاجتك، على الرغم من أنه يجب أن يكون نوع كائن موصوفًا بـ type أو interface. يمكنك التعرف على كيفية وصف TypeScript للكائنات في أنواع الكائنات لكن قد تكون مهتمًا أيضًا باستخدام أنواع الاتحاد لوصف prop يمكن أن يكون واحدًا من عدة أنواع مختلفة ودليل إنشاء أنواع من أنواع لحالات الاستخدام الأكثر تقدمًا.

أمثلة Hooks

تعريفات الأنواع من @types/react تتضمن أنواعًا لـ Hooks المدمجة، لذا يمكنك استخدامها في مكوناتك دون أي إعداد إضافي. تم بناؤها لتأخذ في الاعتبار الكود الذي تكتبه في مكونك، لذا ستحصل على أنواع مستنتجة في كثير من الأحيان وبشكل مثالي لن تحتاج إلى التعامل مع تفاصيل توفير الأنواع.

ومع ذلك، يمكننا إلقاء نظرة على بعض الأمثلة حول كيفية توفير أنواع لـ Hooks.

useState

الـ useState Hook سيعيد استخدام القيمة الممررة كحالة أولية لتحديد ما يجب أن يكون نوع القيمة. على سبيل المثال:

// استنتاج النوع كـ "boolean" const [enabled, setEnabled] = useState(false);

سيعين هذا نوع boolean لـ enabled، و setEnabled ستكون دالة تقبل إما معامل boolean، أو دالة تُرجع boolean. إذا كنت ترغب في توفير نوع صريح للحالة، يمكنك القيام بذلك من خلال توفير معامل نوع لاستدعاء useState:

// تعيين النوع بشكل صريح إلى "boolean" const [enabled, setEnabled] = useState<boolean>(false);

هذا ليس مفيدًا جدًا في هذه الحالة، لكن حالة شائعة قد ترغب فيها في توفير نوع هي عندما يكون لديك نوع اتحاد. على سبيل المثال، status هنا يمكن أن يكون واحدًا من عدة strings مختلفة:

type Status = "idle" | "loading" | "success" | "error"; const [status, setStatus] = useState<Status>("idle");

أو، كما هو موصى به في مبادئ هيكلة الحالة، يمكنك تجميع الحالات ذات الصلة ككائن ووصف الاحتمالات المختلفة عبر أنواع الكائنات:

type RequestState = | { status: 'idle' } | { status: 'loading' } | { status: 'success', data: any } | { status: 'error', error: Error }; const [requestState, setRequestState] = useState<RequestState>({ status: 'idle' });

useReducer

الـ useReducer Hook هو Hook أكثر تعقيدًا يأخذ دالة reducer وحالة أولية. يتم استنتاج الأنواع لدالة reducer من الحالة الأولية. يمكنك اختياريًا توفير معامل نوع لاستدعاء useReducer لتوفير نوع للحالة، ولكن غالبًا ما يكون من الأفضل تعيين النوع على الحالة الأولية بدلاً من ذلك:

import {useReducer} from 'react'; interface State { count: number }; type CounterAction = | { type: "reset" } | { type: "setCount"; value: State["count"] } const initialState: State = { count: 0 }; function stateReducer(state: State, action: CounterAction): State { switch (action.type) { case "reset": return initialState; case "setCount": return { ...state, count: action.value }; default: throw new Error("Unknown action"); } } export default function App() { const [state, dispatch] = useReducer(stateReducer, initialState); const addFive = () => dispatch({ type: "setCount", value: state.count + 5 }); const reset = () => dispatch({ type: "reset" }); return ( <div> <h1>Welcome to my counter</h1> <p>Count: {state.count}</p> <button onClick={addFive}>Add 5</button> <button onClick={reset}>Reset</button> </div> ); }

نحن نستخدم TypeScript في بعض الأماكن الرئيسية:

  • interface State يصف شكل حالة reducer.
  • type CounterAction يصف الإجراءات المختلفة التي يمكن إرسالها إلى reducer.
  • const initialState: State يوفر نوعًا للحالة الأولية، وأيضًا النوع الذي يستخدمه useReducer بشكل افتراضي.
  • stateReducer(state: State, action: CounterAction): State يعين الأنواع لمعاملات دالة reducer وقيمة الإرجاع.

بديل أكثر وضوحًا لتعيين النوع على initialState هو توفير معامل نوع لـ useReducer:

import { stateReducer, State } from './your-reducer-implementation'; const initialState = { count: 0 }; export default function App() { const [state, dispatch] = useReducer<State>(stateReducer, initialState); }

useContext

الـ useContext Hook هو تقنية لتمرير البيانات عبر شجرة المكونات دون الحاجة لتمرير props عبر المكونات. يتم استخدامه من خلال إنشاء مكون موفر وغالبًا من خلال إنشاء Hook لاستهلاك القيمة في مكون فرعي.

يتم استنتاج نوع القيمة المقدمة من السياق من القيمة الممررة إلى استدعاء createContext:

import { createContext, useContext, useState } from 'react'; type Theme = "light" | "dark" | "system"; const ThemeContext = createContext<Theme>("system"); const useGetTheme = () => useContext(ThemeContext); export default function MyApp() { const [theme, setTheme] = useState<Theme>('light'); return ( <ThemeContext.Provider value={theme}> <MyComponent /> </ThemeContext.Provider> ) } function MyComponent() { const theme = useGetTheme(); return ( <div> <p>Current theme: {theme}</p> </div> ) }

تعمل هذه التقنية عندما يكون لديك قيمة افتراضية منطقية - ولكن هناك حالات أحيانًا لا تكون لديك فيها، وفي هذه الحالات قد يبدو null معقولاً كقيمة افتراضية. ومع ذلك، للسماح لنظام الأنواع بفهم الكود الخاص بك، تحتاج إلى تعيين ContextShape | null بشكل صريح على createContext.

هذا يسبب مشكلة أنك تحتاج إلى إزالة | null في النوع لمستهلكي السياق. نوصي بأن يقوم Hook بفحص وجوده في وقت التشغيل ويطرح خطأ عندما لا يكون موجودًا:

import { createContext, useContext, useState, useMemo } from 'react'; // هذا مثال أبسط، لكن يمكنك تخيل كائن أكثر تعقيدًا هنا type ComplexObject = { kind: string }; // يتم إنشاء السياق مع `| null` في النوع، لتعكس بدقة القيمة الافتراضية. const Context = createContext<ComplexObject | null>(null); // سيتم إزالة `| null` عبر الفحص في Hook. const useGetComplexObject = () => { const object = useContext(Context); if (!object) { throw new Error("useGetComplexObject must be used within a Provider") } return object; } export default function MyApp() { const object = useMemo(() => ({ kind: "complex" }), []); return ( <Context.Provider value={object}> <MyComponent /> </Context.Provider> ) } function MyComponent() { const object = useGetComplexObject(); return ( <div> <p>Current object: {object.kind}</p> </div> ) }

useMemo

الـ useMemo Hooks سينشئ/يعيد الوصول إلى قيمة محفوظة في الذاكرة من استدعاء دالة، ويعيد تشغيل الدالة فقط عندما تتغير التبعيات الممررة كمعامل ثانٍ. يتم استنتاج نتيجة استدعاء Hook من قيمة الإرجاع من الدالة في المعامل الأول. يمكنك أن تكون أكثر وضوحًا من خلال توفير معامل نوع لـ Hook.

// يتم استنتاج نوع visibleTodos من قيمة إرجاع filterTodos const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);

useCallback

الـ useCallback يوفر مرجعًا مستقرًا لدالة طالما أن التبعيات الممررة إلى المعامل الثاني هي نفسها. مثل useMemo، يتم استنتاج نوع الدالة من قيمة إرجاع الدالة في المعامل الأول، ويمكنك أن تكون أكثر وضوحًا من خلال توفير معامل نوع لـ Hook.

const handleClick = useCallback(() => { // ... }, [todos]);

عند العمل في وضع TypeScript strict، يتطلب useCallback إضافة أنواع للمعاملات في callback الخاص بك. هذا لأن نوع callback يتم استنتاجه من قيمة إرجاع الدالة، وبدون المعاملات لا يمكن فهم النوع بالكامل.

اعتمادًا على تفضيلات أسلوب الكود الخاص بك، يمكنك استخدام دوال *EventHandler من أنواع React لتوفير النوع لمعالج الحدث في نفس الوقت الذي تحدد فيه callback:

import { useState, useCallback } from 'react'; export default function Form() { const [value, setValue] = useState("Change me"); const handleChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => { setValue(event.currentTarget.value); }, [setValue]) return ( <> <input value={value} onChange={handleChange} /> <p>Value: {value}</p> </> ); }

أنواع مفيدة

هناك مجموعة واسعة جدًا من الأنواع التي تأتي من حزمة @types/react، يستحق القراءة عندما تشعر بالراحة مع كيفية تفاعل React و TypeScript. يمكنك العثور عليها في مجلد React في DefinitelyTyped. سنغطي بعض الأنواع الأكثر شيوعًا هنا.

أحداث DOM

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

import { useState } from 'react'; export default function Form() { const [value, setValue] = useState("Change me"); function handleChange(event: React.ChangeEvent<HTMLInputElement>) { setValue(event.currentTarget.value); } return ( <> <input value={value} onChange={handleChange} /> <p>Value: {value}</p> </> ); }

There are many types of events provided in the React types - the full list can be found here which is based on the most popular events from the DOM.

When determining the type you are looking for you can first look at the hover information for the event handler you are using, which will show the type of the event.

If you need to use an event that is not included in this list, you can use the React.SyntheticEvent type, which is the base type for all events.

Children

هناك طريقتان شائعتان لوصف children المكون. الأولى هي استخدام نوع React.ReactNode، وهو اتحاد من جميع الأنواع الممكنة التي يمكن تمريرها كـ children في JSX:

interface ModalRendererProps { title: string; children: React.ReactNode; }

This is a very broad definition of children. The second is to use the React.ReactElement type, which is only JSX elements and not JavaScript primitives like strings or numbers:

interface ModalRendererProps { title: string; children: React.ReactElement; }

Note, that you cannot use TypeScript to describe that the children are a certain type of JSX elements, so you cannot use the type-system to describe a component which only accepts <li> children.

You can see an example of both React.ReactNode and React.ReactElement with the type-checker in this TypeScript playground.

Style Props

When using inline styles in React, you can use React.CSSProperties to describe the object passed to the style prop. This type is a union of all the possible CSS properties, and is a good way to ensure you are passing valid CSS properties to the style prop, and to get auto-complete in your editor.

interface MyComponentProps { style: React.CSSProperties; }

Further learning

This guide has covered the basics of using TypeScript with React, but there is a lot more to learn. Individual API pages on the docs may contain more in-depth documentation on how to use them with TypeScript.

We recommend the following resources:

  • The TypeScript handbook is the official documentation for TypeScript, and covers most key language features.

  • The TypeScript release notes cover new features in depth.

  • React TypeScript Cheatsheet is a community-maintained cheatsheet for using TypeScript with React, covering a lot of useful edge cases and providing more breadth than this document.

  • TypeScript Community Discord is a great place to ask questions and get help with TypeScript and React issues.

السابقتجهيز المحرر
التاليأدوات مطوري React

Copyright © Meta Platforms, Inc
no uwu plz
uwu?
Logo by@sawaratsuki1004
تعلم React
بداية سريعة
التثبيت
وصف واجهة المستخدم (UI)
إضافة التفاعلية
إدارة State
مخارج الطوارئ
مرجع API
React APIs
React DOM APIs
المجتمع
ميثاق السلوك
تعرف على الفريق
المساهمون في التوثيق
شكر وتقدير
المزيد
المدونة
React Native
الخصوصية
الشروط
ForkTypeScript Playground
function MyButton({ title }: { title: string }) {
  return (
    <button>{title}</button>
  );
}

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton title="I'm a button" />
    </div>
  );
}

ForkTypeScript Playground
interface MyButtonProps {
  /** The text to display inside the button */
  title: string;
  /** Whether the button can be interacted with */
  disabled: boolean;
}

function MyButton({ title, disabled }: MyButtonProps) {
  return (
    <button disabled={disabled}>{title}</button>
  );
}

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton title="I'm a disabled button" disabled={true}/>
    </div>
  );
}

// استنتاج النوع كـ "boolean"
const [enabled, setEnabled] = useState(false);
// تعيين النوع بشكل صريح إلى "boolean"
const [enabled, setEnabled] = useState<boolean>(false);
type Status = "idle" | "loading" | "success" | "error";

const [status, setStatus] = useState<Status>("idle");
type RequestState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success', data: any }
| { status: 'error', error: Error };

const [requestState, setRequestState] = useState<RequestState>({ status: 'idle' });
ForkTypeScript Playground
import {useReducer} from 'react';

interface State {
   count: number 
};

type CounterAction =
  | { type: "reset" }
  | { type: "setCount"; value: State["count"] }

const initialState: State = { count: 0 };

function stateReducer(state: State, action: CounterAction): State {
  switch (action.type) {
    case "reset":
      return initialState;
    case "setCount":
      return { ...state, count: action.value };
    default:
      throw new Error("Unknown action");
  }
}

export default function App() {
  const [state, dispatch] = useReducer(stateReducer, initialState);

  const addFive = () => dispatch({ type: "setCount", value: state.count + 5 });
  const reset = () => dispatch({ type: "reset" });

  return (
    <div>
      <h1>Welcome to my counter</h1>

      <p>Count: {state.count}</p>
      <button onClick={addFive}>Add 5</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

import { stateReducer, State } from './your-reducer-implementation';

const initialState = { count: 0 };

export default function App() {
const [state, dispatch] = useReducer<State>(stateReducer, initialState);
}
ForkTypeScript Playground
import { createContext, useContext, useState } from 'react';

type Theme = "light" | "dark" | "system";
const ThemeContext = createContext<Theme>("system");

const useGetTheme = () => useContext(ThemeContext);

export default function MyApp() {
  const [theme, setTheme] = useState<Theme>('light');

  return (
    <ThemeContext.Provider value={theme}>
      <MyComponent />
    </ThemeContext.Provider>
  )
}

function MyComponent() {
  const theme = useGetTheme();

  return (
    <div>
      <p>Current theme: {theme}</p>
    </div>
  )
}

import { createContext, useContext, useState, useMemo } from 'react';

// هذا مثال أبسط، لكن يمكنك تخيل كائن أكثر تعقيدًا هنا
type ComplexObject = {
kind: string
};

// يتم إنشاء السياق مع `| null` في النوع، لتعكس بدقة القيمة الافتراضية.
const Context = createContext<ComplexObject | null>(null);

// سيتم إزالة `| null` عبر الفحص في Hook.
const useGetComplexObject = () => {
const object = useContext(Context);
if (!object) { throw new Error("useGetComplexObject must be used within a Provider") }
return object;
}

export default function MyApp() {
const object = useMemo(() => ({ kind: "complex" }), []);

return (
<Context.Provider value={object}>
<MyComponent />
</Context.Provider>
)
}

function MyComponent() {
const object = useGetComplexObject();

return (
<div>
<p>Current object: {object.kind}</p>
</div>
)
}
// يتم استنتاج نوع visibleTodos من قيمة إرجاع filterTodos
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
const handleClick = useCallback(() => {
// ...
}, [todos]);
import { useState, useCallback } from 'react';

export default function Form() {
const [value, setValue] = useState("Change me");

const handleChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => {
setValue(event.currentTarget.value);
}, [setValue])

return (
<>
<input value={value} onChange={handleChange} />
<p>Value: {value}</p>
</>
);
}
ForkTypeScript Playground
import { useState } from 'react';

export default function Form() {
  const [value, setValue] = useState("Change me");

  function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
    setValue(event.currentTarget.value);
  }

  return (
    <>
      <input value={value} onChange={handleChange} />
      <p>Value: {value}</p>
    </>
  );
}

interface ModalRendererProps {
title: string;
children: React.ReactNode;
}
interface ModalRendererProps {
title: string;
children: React.ReactElement;
}
interface MyComponentProps {
style: React.CSSProperties;
}