Reactjs - القيمة الافتراضية للخاصية في مكون React باستخدام TypeScript
لا أستطيع معرفة كيفية تعيين القيم الافتراضية للخصائص لمكوناتي باستخدام تايب سكريبت.
هذا هو كود المصدر:
class PageState
{
}
export class PageProps
{
foo: string = "bar";
}
export class PageComponent extends React.Component<PageProps, PageState>
{
public render(): JSX.Element
{
return (
<span>Hello, world</span>
);
}
}
وعندما أحاول استخدام المكون بهذه الطريقة:
ReactDOM.render(<PageComponent />, document.getElementById("page"));
أتلقى خطأ يقول إن الخاصية foo مفقودة. أريد استخدام القيمة الافتراضية. لقد حاولت أيضًا استخدام static defaultProps=... داخل المكون، لكنه لم يكن له تأثير كما توقعت.
src/typescript/main.tsx(8,17): error TS2324: Property 'foo' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<PageComponent> & PageProps & { children?: ReactEle...'.
كيف يمكنني استخدام قيم الخصائص الافتراضية؟ العديد من مكونات جافا سكريبت التي تستخدمها شركتي تعتمد عليها وعدم استخدامها ليس خيارًا.
الإجابة رقم 1
القيم الافتراضية للخصائص مع مكون الفئة
استخدام static defaultProps صحيح. يجب عليك أيضًا استخدام الواجهات (interfaces)، وليس الفئات (classes)، للـ props والحالة (state).
تحديث 2018/12/1: لقد حسّن TypeScript الفحص النوعي المتعلق بـ defaultProps مع مرور الوقت. تابع القراءة للاطلاع على أحدث وأفضل الاستخدامات وصولاً إلى الاستخدامات والمشاكل القديمة.
لـ TypeScript 3.0 وما فوق
أضافت TypeScript دعمًا محددًا لـ defaultProps لجعل التحقق من الأنواع يعمل كما تتوقع. مثال:
interface PageProps {
foo: string;
bar: string;
}
export class PageComponent extends React.Component<PageProps, {}> {
public static defaultProps = {
foo: "default"
};
public render(): JSX.Element {
return (
<span>Hello, { this.props.foo.toUpperCase() }</span>
);
}
}
والذي يمكن عرضه وتكويده دون تمرير سمة foo:
<PageComponent bar={ "hello" } />
لاحظ أن:
fooغير معرف كاختياري (foo?: string) حتى وإن لم يكن مطلوبًا كخاصية في JSX. تعريفه كاختياري يعني أنه يمكن أن يكونundefined، لكن في الحقيقة لن يكون أبدًاundefinedلأنdefaultPropsيوفر قيمة افتراضية. فكر في الأمر بطريقة مشابهة لكيفية جعل معلمة الدالة اختيارية، أو لديها قيمة افتراضية، لكن ليس كلا الأمرين معًا، ومع ذلك يعني كلاهما أن الاستدعاء لا يحتاج لتحديد قيمة. TypeScript 3.0+ تتعامل معdefaultPropsبطريقة مشابهة، وهذا فعلاً رائع لمستخدمي React!- لا تحتوي
defaultPropsعلى توضيح نوع صريح. يتم استنتاج نوعها ويستخدمه المترجم لتحديد السمات المطلوبة في JSX. يمكنك استخدامdefaultProps: Pick<PageProps, "foo">لضمان أنdefaultPropsتتطابق مع جزء منPageProps. المزيد حول هذا التحذير موضح هنا. - هذا يتطلب أن تكون نسخة
@types/reactهي16.4.11لكي تعمل بشكل صحيح.
لـ TypeScript 2.1 حتى 3.0
قبل أن يقوم TypeScript 3.0 بتنفيذ دعم المترجم لـ defaultProps، كان بإمكانك الاستمرار في استخدامه وكان يعمل 100% مع React أثناء التشغيل، ولكن بما أن TypeScript كان يعتبر الخصائص فقط عند التحقق من سمات JSX، فسوف تضطر إلى وسم الخصائص التي لها قيم افتراضية كاختيارية باستخدام ?. مثال:
interface PageProps {
foo?: string;
bar: number;
}
export class PageComponent extends React.Component<PageProps, {}> {
public static defaultProps: Partial<PageProps> = {
foo: "default"
};
public render(): JSX.Element {
return (
<span>Hello, world</span>
);
}
}
لاحظ أن:
- من الجيد وضع تعليقات توضيحية على
defaultPropsباستخدامPartial<>بحيث يتم التحقق من النوع مقابل الخصائص الخاصة بك، لكن ليس عليك توفير كل خاصية مطلوبة بقيمة افتراضية، وهذا قد لا يكون منطقيًا لأن الخصائص المطلوبة لا يجب أن تحتاج أبدًا إلى قيمة افتراضية. - عند استخدام
strictNullChecks، ستكون قيمةthis.props.fooقد تكون غير معرفةوتتطلب تأكيدًا على أنها ليست فارغة (أيthis.props.foo!) أو حماية نوع (أيif (this.props.foo) ...) لإزالةundefined. هذا مزعج لأن القيمة الافتراضية للخاصية تعني أنها في الواقع لن تكون غير معرفة أبدًا، لكن TypeScript لم يفهم هذا التدفق. هذا أحد الأسباب الرئيسية التي دفعت TypeScript 3.0 لإضافة دعم صريح لـdefaultProps.
قبل TypeScript 2.1
يعمل هذا بنفس الطريقة لكن لا يوجد لديك أنواع Partial، لذلك فقط احذف Partial<> وقم إما بتزويد قيم افتراضية لجميع الخصائص المطلوبة (حتى لو لم تُستخدم هذه القيم الافتراضية أبدًا) أو احذف شرح النوع الصريح تمامًا.
القيم الافتراضية للخصائص مع المكونات الوظيفية
يمكنك استخدام defaultProps على مكونات الدالة أيضًا، لكن عليك كتابة نوع دالتك على واجهة FunctionComponent (StatelessComponent في @types/react قبل الإصدار 16.7.2) حتى يعرف TypeScript عن defaultProps على الدالة:
interface PageProps {
foo?: string;
bar: number;
}
const PageComponent: FunctionComponent<PageProps> = (props) => {
return (
<span>Hello, {props.foo}, {props.bar}</span>
);
};
PageComponent.defaultProps = {
foo: "default"
};
لاحظ أنه لا عليك استخدام Partial<PageProps> في أي مكان لأن FunctionComponent.defaultProps محدد بالفعل كجزئي في TS 2.1+.
خيار آخر جيد (هذا ما أستخدمه) هو تفكيك معلمات props وتعيين القيم الافتراضية مباشرة:
const PageComponent: FunctionComponent<PageProps> = ({foo = "default", bar}) => {
return (
<span>Hello, {foo}, {bar}</span>
);
};
إذن أنت لا تحتاج إلى defaultProps على الإطلاق! كن على علم أنه إذا قمت بتوفير defaultProps على مكون دالة، فإنها ستأخذ الأولوية على قيم المعاملات الافتراضية، لأن React ستمرر دائمًا قيم defaultProps صراحةً (لذلك المعاملات لن تكون undefined أبدًا، وبالتالي لن يتم استخدام المعامل الافتراضي). لذلك ستستخدم إما أحدهما أو الآخر، وليس كلاهما.
الإجابة رقم 2
مع TypeScript 2.1 وما فوق، استخدم Partial < T > بدلاً من جعل خصائص الواجهة اختيارية.
export interface Props {
obj: Model,
a: boolean
b: boolean
}
public static defaultProps: Partial<Props> = {
a: true
};
الإجابة رقم 3
مكون وظيفي
في الواقع، بالنسبة للمكون الوظيفي، أفضل ممارسة هي كما يلي، لقد أنشأت مكون Spinner تجريبي:
import React from 'react';
import { ActivityIndicator } from 'react-native';
import { colors } from 'helpers/theme';
export interface SpinnerProps {
color?: string;
size?: 'small' | 'large' | 1 | 0;
animating?: boolean;
hidesWhenStopped?: boolean;
}
const Spinner = ({
color = colors.primary,
size = 'small',
animating = true,
hidesWhenStopped = true,
}: SpinnerProps): JSX.Element => (
<ActivityIndicator
color={color}
size={size}
animating={animating}
hidesWhenStopped={hidesWhenStopped}
/>
);
export default Spinner;
إذا كان المكون الخاص بك يحتوي على children فمن الأفضل استخدام React.FC، كما يلي:
export interface TypographyProps {
color?: string;
}
const Typography: React.FC<TypographyProps> = ({
children,
color,
}) => (
<span style={{ color }}>
{children}
</span>
);
export default Typography;
الإجابة رقم 4
يمكنك استخدام عامل الانتشار لإعادة تعيين الخصائص مع مكون وظيفي قياسي. الشيء الذي يعجبني في هذا النهج هو أنه يمكنك مزج الخصائص المطلوبة مع الخصائص الاختيارية التي لها قيمة افتراضية.
interface MyProps {
text: string;
optionalText?: string;
}
const defaultProps = {
optionalText = "foo";
}
const MyComponent = (props: MyProps) => {
props = { ...defaultProps, ...props }
}
الإجابة رقم 5
مع Typescript 3.0 هناك حل جديد لهذه المشكلة:
export interface Props {
name: string;
}
export class Greet extends React.Component<Props> {
render() {
const { name } = this.props;
return <div>Hello ${name.toUpperCase()}!</div>;
}
static defaultProps = { name: "world"};
}
// Type-checks! No type assertions needed!
let el = <Greet />
لاحظ أنه لكي يعمل هذا، تحتاج إلى إصدار أحدث من @types/react من 16.4.6. إنه يعمل مع 16.4.11.
