Definitelytyped: React.d.ts للقراءة فقط<t>في الدولة والدعائم</t>

تم إنشاؤها على ٢٥ يناير ٢٠١٧  ·  91تعليقات  ·  مصدر: DefinitelyTyped/DefinitelyTyped

مرحبًا ericanderson

أواجه العديد من المشكلات مع هذا التغيير عند استخدامه عمليًا:

المشكلة 1: اذهب إلى التعريف

عند الضغط على Go To Definition (الانتقال إلى التعريف) على خاصية props أو state ، يتعذر على Typescript حلها.

interface MyComponentProps {
    name: string;
}

export abstract class MyComponent extends React.Component<MyComponentProps , void> {
    myMethood() {
       this.props.name; //<-- Go To definition in name
   }
}

image

هذا منطقي لأن العضو تم إنشاؤه صناعيًا ، ولكنه مزعج على أي حال.

المشكلة 2: التسلسلات الهرمية للمكونات (عام P مع قيود)

الأهم من ذلك ، إذا قمت بإنشاء مكون مجرد مثل هذا:

interface MyBaseProps {
    onChange?: (val: any) => void;
}

export abstract class MyBase<P extends MyBaseProps> extends React.Component<P, void> {
    myMethood() {
        this.props.onChange!(2); //The type is S["onChange"] instead of (val: any) => void and so is not invocable. 
   }
}

TS قادر على إظهار أن هناك خاصية عند التغيير ، لكن في بعض الأحيان لا يستطيع اكتشاف نوعه.

image

هذا هو أهم تغيير لأنه إذا كان يمنعني من الحصول على تسلسلات هرمية للمكونات التي تشترك في الدعائم والوظائف المشتركة. يبدو أنه مشكلة في مترجم TS ولكن حتى يتم إصلاحها.

المشكلة 3: ليس للقراءة فقط.

بينما أوافق على أن هذا التغيير يلتقط جيدًا الهدف الوظيفي لـ React ، فهناك مواقف صحيحة حيث يمكنك تعديل الحالة بشكل إلزامي ، كما هو الحال في المُنشئ ، وأيضًا إذا قمت بتغيير الحالة واستدعاء forceUpdate يعمل دائمًا على ما يرام.

C# this.state.name = "John"; this.forceUpdate(); //Ok as long as you don't setState afterwards, but calling setState also is annoying with the callback.

هل هو موصى به؟ رقم.
هل هو ممنوع؟ أيضًا لا ، وإلا فلن توجد ForceUpdate.

بالطبع يمكنك تحويل الولاية إلى S (أو any ) وإجراء التغيير ، لكن إذا كان النمط الشائع يصبح مرهقًا.

الخلاصة: هل تستحق؟

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

على الجانب الآخر ، تغيير setState رائع 👍 ، لم أكن أعرف عن Pick<S,K> .

التعليق الأكثر فائدة

أعتقد أن المشكلة 3 مطروحة للنقاش.

أنت محق في أنه يمكنك _تقنيًا_ عمل المثال أعلاه في React ، لكنني سأجادل بالتأكيد أن هذه ليست الطريقة التي كان يُقصد من استخدام React بها.

يمكن تقسيم هذا إلى 3 حالات متميزة.

تهيئة عامة

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  public state: State = {
    bar: 5,
  };
}

التهيئة على أساس الدعائم

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      bar: props.baz,
    };

    // or
    this.setState({
      bar: props.baz,
    });
  }
}

مهمة عشوائية forceUpdate

نظرًا لأنني أعتقد أنه من الأفضل دفع الأشخاص نحو الشيء "الصحيح" ، يمكنك بسهولة حل هذه المشكلة عن طريق إعادة إعلان public state :

interface State {
  bar: number;
}

class Foo extends React.Component<{}, State> {
  public state: State;
  public myMethod() {
    this.state.bar = 5;
  }
}

ال 91 كومينتر

ما هو إصدار المطبوع على الحروف الذي يستخدمه الاستوديو المرئي الخاص بك؟

vsaio لسا

بالنسبة للمشكلة 1 ، مع TS 2.1.5 وأحدث VSCode ، هذا يعمل بشكل جيد بالنسبة لي. ليس لدي windows / VS لذا لا يمكنني التحقق هناك ، لكنني أراهن أن هناك تحديثات للمكونات الإضافية أو أنك لست على TS 2.1.5

نفس الشيء بالنسبة للمشكلة 2

VS 2015 مع TS 2.1.5.0

أعتقد أن المشكلة 3 مطروحة للنقاش.

أنت محق في أنه يمكنك _تقنيًا_ عمل المثال أعلاه في React ، لكنني سأجادل بالتأكيد أن هذه ليست الطريقة التي كان يُقصد من استخدام React بها.

يمكن تقسيم هذا إلى 3 حالات متميزة.

تهيئة عامة

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  public state: State = {
    bar: 5,
  };
}

التهيئة على أساس الدعائم

interface State {
  bar: number;
}

interface Props {
  baz: number;
}

class Foo extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      bar: props.baz,
    };

    // or
    this.setState({
      bar: props.baz,
    });
  }
}

مهمة عشوائية forceUpdate

نظرًا لأنني أعتقد أنه من الأفضل دفع الأشخاص نحو الشيء "الصحيح" ، يمكنك بسهولة حل هذه المشكلة عن طريق إعادة إعلان public state :

interface State {
  bar: number;
}

class Foo extends React.Component<{}, State> {
  public state: State;
  public myMethod() {
    this.state.bar = 5;
  }
}

مشكلتي مع تباين الأدوية. خصيصًا للكتابة داخل الفصل الذي يتم كتابته بشكل عام. أدناه عينة صغيرة جدًا من الأماكن التي تتعطل فيها الأشياء.

class TBaseState {
  public value: string;
}

function globalFunc<T extends Readonly<TBaseState>>(item: T) {
}

class MyComponent<TProps, TState extends TBaseState> extends React.Component<TProps, TState> {
  broken() {
    // typing of this.state is Readonly<TState>
    // this is not assignable to Readonly<TBase>
    globalFunc(this.state);

    // this is a horrible hack to fix the generics variance issue
    globalFunc(this.state as TState as Readonly<TBaseState>);
  }
}

class MyState extends TBaseState {
}

let component: MyComponent<any, MyState>;

// here the typing of component.state is Readonly<MyState>
// this is assignable to Readonly<TBase>
globalFunc(component.state);

أنا في TS 2.1.5.0

image

ولكن يمكن أن يكون لدينا في VS تجربة TS أسوأ مما كانت عليه في رمز VS ...

بالنسبة للمشكلة 1 ، انتقل إلى التعريف لا يعمل TS أيضًا في رمز VS:

interface MyComponentProps {
    name: string;
}

export abstract class MyComponent extends React.Component<MyComponentProps , void> {
    fullName: string;
    myMethood() {
       this.props.name; //<-- doesnt work
       this.fullName; //<-- works
   }
}

بالنسبة للمشكلة 2 ، من الصحيح أن VS Code يتصرف بشكل أفضل:

image

بينما يبدو VS مرتبكًا:

image

أعتقد أنه بالنسبة لـ VSCode والمشكلة 1 ، يعمل هذا لأنني أستخدم المكون الإضافي لـ "أحدث نسخة من القواعد النحوية وجافا سكريبت" والتي يجب أن تتمتع بمعالجة أكثر ذكاءً.

patsissons هذا مثال مثير للاهتمام ، على الرغم من أنني أعتقد أنه يمثل خطأ في الكتابة المطبوعة أكثر من خطأ في ملف التعريف. على سبيل المثال ، استخدم setState لأخذ S وهو ما يعني القيام ببعض الحيل التي اعتدنا عليها القيام بحيل غريبة مثل setState({foo:5} as any as State) أو استخدام الحيل التي تأخذ وظيفة. لست متأكدًا من أن الافتقار إلى التعبيرية للمترجم يجعل الكتابة "خاطئة". أعتقد أن هذه حجة جيدة لإجراء تغيير في README لوضع علامة على حالة الحافة هذه.

هل قمت بتقديم مشكلة بخصوص TS؟

لذا فإن هذا التغيير في الوقت الحاضر يكسر جميع VS ويعطل Go To Definition في جميع رموز VS إلا إذا كان لديك مكون إضافي ...

هناك أيضا حجة الاكتمال. هناك عدد قليل من واجهات برمجة التطبيقات التي من المفترض أن تكون للقراءة فقط وليست في الوقت الحاضر ، فقط في React.d.ts

 interface ComponentLifecycle<P, S> {
        componentWillMount?(): void;
        componentDidMount?(): void;
        componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
        shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: Readonly<any>): boolean;
        componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: Readonly<any>): void;
        componentDidUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>, prevContext: Readonly<any>): void;
        componentWillUnmount?(): void;
    }

أعتقد أنه يجب استخدام "القراءة فقط" لـ "التجميد" أو "Inmmutable.js" وليس للذيل الطويل من الأفكار التي لا يُقصد تعديلها ، مثل كائنات الأحداث ، على سبيل المثال.

لم يتم التقديم ، لقد قمت فقط بتعديل الكود الخاص بي اليوم للتعامل مع أنواع Readonly<T> الجديدة ، كانت هذه حالة واجهتني ولم يكن لدي حل مكتوب بشكل صحيح. انطلق وقدم مشكلة ، سأكون مشغولاً معظم اليوم ببقية كود الترقيع.

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

بالنسبة إلى فواصل VS ، لا أعرف ما هو الصواب. هل يجب تأجيل الأنواع لأن بعض الأدوات لا يتم تحديثها؟

patsissons يمكنك دائمًا تقديم كتاباتك الخاصة للتفاعل الآن إذا كنت تريد الانتظار لترى كيف سينتهي هذا الأمر قبل تحديث كل التعليمات البرمجية الخاصة بك. https://ericlanderson.com/using-custom-typescript-definitions-with-ts-2-x-3121db84015d#.ftlkojwnb

في تجربتنا ، VS دائمًا متخلف قليلاً. يستخدم متجرنا vscode للقيام بأي تطوير فعال للنص المطبوع ويتم استخدام VS بشكل أكبر لمجرد تصحيح ملفات التعليمات البرمجية أو للمطورين الذين لا يستخدمون النصوص المطبوعة للبحث في التعليمات البرمجية ، وليس بالضرورة من أجل التطوير النشط.

ericanderson أن الاختراق ليس سيئًا للغاية في الوقت الحالي ، فأنا فقط بحاجة إلى تذويب Readonly<T> للحصول على T الذي يمكن تخصيصه لـ Readonly<Base> .

نحن نتحدث عن "رد فعل d.ts" ، يتم استخدام تصريح العضو الواحد هذا على نطاق واسع. أعتقد أن الأمر يستحق التراجع حتى يصبح VS جاهزًا.

أيضًا ، مثل 50٪ من الأنواع في العالم من المفترض أن تكون للقراءة فقط ، مثل الكائنات التي تحصل عليها من واجهات برمجة التطبيقات ، لا أعتقد أننا بحاجة إلى إضافة تعليق توضيحي لذلك.

أعتقد أنه يجب استخدام Readonly للكائنات التي تم تحويلها صراحةً إلى خصائص get-only. مثل التجميد.

olmobrutall Readonly جديد ، لذا لم يتم تحديد أفضل الممارسات بالضبط. أنا شخصياً أفضل أن يعلن كل شيء أن الأمر يتطلب Readonly<> من الأشياء للمساعدة في الإشارة إلى أنه لن يغيرها. وبالمثل ، لا تتوقع منك React تعديل state خارج setState وبالتالي يضمن هذا التغيير أن الحوادث لا تسبب أخطاء ، وهي إحدى الفوائد الأساسية لاستخدام TypeScript على جافا سكريبت.

إذا كان الأداء أكثر اتساقًا عبر المتصفحات مقابل Object.freeze ، أتخيل أن أعضاء React سيبدأون بالفعل في التجمد بعد setState .

ما هو الغرض من ForceUpdate إذن؟

أشعر بالفضول حيال أفكار الأشخاص الآخرين حول كيفية عمل DefinitelyTyped فيما يتعلق بتحديثات الأدوات بالإضافة إلى فلسفة حول Readonly في رد الفعل (والمكتبات الأخرى التي تهدف إلى عدم تعديل كائنات معينة).

cc / johnnyreillyvsaio @ pspeter3 للاطلاع على أفكار حول الرد على وجه التحديد والأفكار الأخرى بشكل عام
cc / @ andy- msmhegazy للحصول على أفكار حول كيف يجب أن يتم تطبيق DefinitelyTyped على العمل فلسفيًا لتحديثات الأدوات والاستخدام الحماسي لـ Readonly

olmobrutall نستخدم forceUpdate لوضع عرض في قائمة الانتظار على جانب التفاعل ، مدفوعًا من الأحداث التي يمكن ملاحظتها على جانب الولاية.

تحديث :
سأوضح السيناريو الخاص بنا قليلاً حتى لا يساء فهمه. كائنات حالتنا هي كائنات غير قابلة للتغيير وتعيش طويلاً (لذا فإن Readonly<T> مناسب جدًا لنا). تحتوي كائنات الحالة هذه على عدة تدفقات يمكن ملاحظتها rxjs والتي توجه إلى إشعار يمكن ملاحظته يسمى stateChanged . تراقب مكونات React هذا الذي يمكن ملاحظته للأحداث وتقوم بتحويل تلك الأحداث إلى مكالمة إلى forceUpdate (بعد الرفض). في الواقع ، تعيش دولتنا المتغيرة داخل الدولة ، لكن الدولة نفسها والأعضاء الموجودون فيها جميعًا غير قابلين للتغيير. هذه بالتأكيد ليست حالة الاستخدام القياسية لـ React ، لكنها متشابهة جدًا في التدفق. لدينا ببساطة كائنات حالة ذكية تعرف كيفية إبلاغ المكونات عند الحاجة إلى إعادة العرض.

ericanderson المشكلة الرئيسية هي أن تعريفات الأنواع هذه تعاني من مشكلات SemVer. نظرًا لأن إصدارات تعريف النوع مرتبطة إلى حد كبير بإصدارات الوحدة النمطية الخاصة بها ، فإننا ننتهي مع نتوء إصدار ثانوي يؤدي إلى تغييرات تعريف النوع المتكسر ، مما يعني أنه يتعين علينا تثبيت إصدارات @types في package.json ملف.

olmobrutall من مستندات الرد:

عادة يجب أن تحاول تجنب كل استخدامات forceUpdate () والقراءة فقط من this.props و this.state في العرض ().

يخبرك دليل التفاعل في الواقع بعدم تحديث الحالة مباشرة: https://facebook.github.io/react/docs/state-and-lifecycle.html#do -not-edit-state-مباشرة

forceUpdate ، كما قرأته ، هو فقط لإجبار المكون الخاص بك على التحديث عندما يعتمد المكون الخاص بك على البيانات _ لا _ في الدعائم الخاصة بك أو حالتك.

patsissons قد أكون مخطئًا ، لكنني أعتقد أن SemVer مصمم ليكون متوافقًا مع واجهات برمجة التطبيقات والأهداف الدلالية. فقط لأنك تستخدم مكتبة بطريقة لم تكن مقصودة (وفقًا للمستندات) لا يعني أن المكتبة يجب أن تستمر في دعم الاستخدامات غير المقصودة المذكورة. مؤلفو المكتبة جيدون في SemVer لتغيير الدلالات التي كانت غير صحيحة ولكن تم استخدامها من قبل بعض الناس.

ومع ذلك ، ربما يكون Readonly<> إلى state تغييرًا كبيرًا جدًا ، لكن افترض للحظة أنه التغيير الصحيح. متى يجب تحريرها في DefinitelyTyped؟ ستعاني شفرتك دائمًا من الحاجة إلى التغيير بمجرد حصولك على التحديث الذي يشير أخيرًا state كـ Readonly<> .

ما زلت لا أعرف ما هو الصحيح حول تطبيق Readonly<> على state مما يجعل من الصعب مناقشة semver أو الأدوات أو أي شيء آخر. كان حدسي أنه كان على حق. الأشخاص الذين راجعوا التغيير لم يطرحوه أبدًا على أنه مشكلة. يبدو أنه يتماشى مع نية فريق React.

يسعدني أن أرجع إلى أي من المراجعين للتفاعل في DefinitelyTyped (لقد قمت بنسخهم جميعًا أعلاه).

لذا فإن المرصد يغير الحالة وتجبر التحديث؟ لذا فإن تغيير الدولة بشكل إلزامي هو بعض الكيفية المسموح بها.

أدرك أن المكان الذي يجب استخدام Readonly فيه لم يتم تحديده بنسبة 100٪. لكن هل نحتاج إلى البدء بخاصية مثيرة للجدل ومستخدمة على نطاق واسع قبل أن تصبح الأدوات جاهزة؟

أنا جميعًا مؤيد للطباعة بشدة ، فأنا أدير 6 مشاريع جميعها مع تشيكات صارمة ، لكن الفوائد هنا أقل بكثير من المشكلات التي تنتجها حاليًا.

ericanderson أعتقد أن SemVer2 مصمم للسماح بإعلانات إصدار العقدة مثل ^15.0.0 وتوقع أن تكون أي ترقيات ثانوية أو تصحيح (على سبيل المثال ، 15.0.1 أو 15.1.0 ) للوحدة شفافة أو متوافق مع الإصدارات السابقة على الأقل من منظور خارجي. ستتطلب أي ترقيات رئيسية ( 16.0.0 ) تعديل إعلان الإصدار لإدخال التغيير. هذه البوابات بشكل أساسي تكسر التغييرات من إحضارها إلى نظام الكتابة. لكن حاليًا لا يمكن لإصدارات تعريف النوع أن تحيد عن الإصدار الرئيسي لإصدارات الوحدة النمطية الخاصة بها (حسب الاصطلاح) ، مما يؤدي إلى هذا الانقطاع.

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

لكنك لن تقوم بعمل ForceUpdate لإزالة العلاقات العامة ، أليس كذلك؟

ثم إذا كان forceUpdate موجودًا ، فيجب أن يكون هناك تغيير حتمي للحالة أيضًا.

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

لحسن الحظ ، تسمح React بمسار scape وإجراء تغييرات مباشرة في الحالة الممكنة ، فهل سنمنع مطوري TS من اتخاذ هذا الطريق؟ أليس هذا أبويًا جدًا؟

على سبيل المثال ، يروج Vue.js للتغييرات الحتمية ، ولن أتفاجأ إذا كان يؤثر على React.

كما أنني كنت أقرأ منشور مدونة لبعض مؤلفي React منذ وقت ليس ببعيد (يمكنني أن أتذكر) يشجع على استخدام React بدون كل حفل Redux.

موقفي بخلاف ذلك هو نشر تعريفات الأنواع في أسرع وقت ممكن ، شاغلي الوحيد هو الأتمتة. نظرًا للحالة الحالية لتعريف النوع ، يمكن أن ينكسر بناء متتالي بدون تغييرات على المصدر. هذه الأنواع من حالات فشل CI غير الحتمية مقلقة. لا أحد يريد أن يرى بنائه ينكسر لأنهم دفعوا التغيير ثم اكتشفوا أن يد التغيير لا علاقة له بكسر البناء.

بعد أن أنام عليها كانت آرائي الشخصية كالتالي:

  • قد تتضمن الممارسة الجيدة استخدام أقفال الغزل أو npm ، لذلك لن تحصل على مفاجأة ما لم تقم بالترقية محليًا أولاً.
  • جعل الحالة للقراءة فقط هي الطريقة التي يقصد بها استخدام React. الوثائق والأمثلة تؤكد ذلك.
  • سير العمل حيث لا تريد استخدام setState يقع ضمن قاعدة التعليمات البرمجية الخاصة بك وضمن حقوقك الخاصة. أنت محق في أن React توفر ForceUpdate ، لكن الغرض من استخدامها هو التسبب في تصيير عندما تكون خارج حالة الاستخدام المقصودة. وبالتالي ، إذا كنت لا تريد استخدام الحالة على النحو المنشود ، فلا بأس بذلك ، ولكن في هذه المرحلة لن تحتاج إلى استخدام حالة متغير المثيل. في الواقع ، يمكنك فقط استخدام المتغيرات الخاصة العادية.
  • نعم ، يعتمد هذا المشروع على الكثير من الأشخاص ، ومع ذلك ، فإن هاتين الشكاويتين الوحيدتين حتى الآن تجعلني أعتقد أن هذه ليست مشكلة منتشرة على نطاق واسع. علاوة على ذلك ، يمكن إعادة كتابة المشكلة التي أثيرت حول الوظيفة العامة لأخذ العام بشكل مختلف (انظر مشكلة TypeScript المرتبطة)

نظرًا للأفكار المذكورة أعلاه بالإضافة إلى الحلول البديلة لتطبيقات React غير القياسية ، أعتقد أن Readonly صحيح وأن التغيير الوحيد المطلوب لتحقيق الاكتمال هو تحديث أساليب دورة الحياة لتتطابق.

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

Patsisson ، باستخدام VS ، VS Code أو أي محرر آخر؟

نقطتي هي أنه بينما تروج React لمقاربة وظيفية ، فإنها تسمح بسير عمل يشبه ألعاب الفيديو حيث يمكنك إجراء تغييرات على العالم بشكل إلزامي (حالة) ثم تقديم (forceUpdate). هذا النهج ممنوع الآن في TS. نوع من func-damentalism :)

سأفكر في بدائل لجعل نظامي البيئي الحالي قابلاً للتطبيق ثم ...

مشكلة 2 طرح الخطأ فقط مع strictNullChecks .

[TS] Cannot invoke an expression whose type lacks a call signature. Type '((val: any) => void) | undefined' has no compatible call signatures.

+1 أنا أستخدم إجراءات صارمة

ericanderson ؟

كما هو مذكور أعلاه ، يتعلق هذا بالأدوات ، والتي من الواضح أنها خارج نطاق DT. إذا كنت تستخدم VSCode وقمت بتثبيت معاينة محلل TS القادم ، فلم أر هذه المشكلة مع strictNullChecks عندما كنت أكتب أيًا مما سبق.

ليس لدي نوافذ لذلك لا يمكنني التحدث إلى VS المناسب.

هذا التصحيح Pick كسر الاقتراحات ضمن VSCode. عند القيام بـ this.setState({ | }) (Ctrl + Space) ، لا يتم عرض أي شيء ، على الرغم من أن الدولة محددة بوضوح واستخدام Partial<State> لأن setState يمكنه تعيين حالة العضو بشكل انتقائي

يجب أن يكون الرمز الصحيح IMHO هو setState(state: Partial<S>, callback?: () => any): void;

كما تمت مناقشته في فرع طلب السحب الأصلي ، بدأنا بـ Partial. ومع ذلك ، إذا كان كائن الدولة الخاص بك هو:

حالة الواجهة {
foo: سلسلة ؛
}

ثم مع جزء يمكنك القيام بما يلي:

setState ({foo: undefined}) ،

وهو أمر خاطئ بشكل واضح.

أنا آسف - ولكن مرة أخرى بخصوص Readonly

React ليست فقط Redux و setState. React هي أيضًا mobx وأنماط أخرى يمكن ملاحظتها ، حيث يكون تخصيص خصائص الحالة هو الميزة الرئيسية . يقتل التغيير الذي تمت مناقشته استخدام mobx بشكل كامل باستخدام الكتابة المطبوعة.

فلماذا تضيف السلوك غير الموجود في كود رد الفعل الأصلي إلى ملف .d.ts؟ يجب أن يكون .d.ts انعكاسًا للمكتبة الأصلية ولكن ليس تعليم الأشخاص أسلوب الترميز الصحيح ، وفقًا لوجهة نظر المؤلف!

lezious ، أخشى أنني لا أفهم موقفك. هل يمكنك تقديم عينة من التعليمات البرمجية وما الذي تم كسره في الكتابة فيما يتعلق بالعينة؟ شكرا!

لا مشكلة:

هذا هو صفي الدولة

class UserInfoBlockState  
{
    <strong i="7">@observable</strong>                  <- this is mobx way to declare state
    public updating: boolean;
    <strong i="8">@observable</strong> 
    public deleted: boolean;
}

وهذا هو المكون الخاص بي

<strong i="12">@observer</strong>       <-- this is mobx way to make component react to state change
export class UserPanel extends React.Component<IUserInfoBlockProps, UserInfoBlockState>
{
   ......
     private updateUser()
    {
        this.state.updating = true;
        UsersAPI.update(this.props.user)
       .then(() =>
            {
                this.state.updating = false;      <--- this is the mobx way to work with the state
            }
        ).catch(() =>
            {
                this.showErrror("Server error");
                this.state.updating = false;
            });
    }
   ....

}

والآن نحن (شركتنا مع مشروع ضخم مكتوب على رد فعل + mobx) قمنا بتحديث DT و React في بداية دائرة الإصدار الجديد و ... أكثر من 3000 خطأ في التجميع "الخاصية للقراءة فقط". رائع. ما الذي تقترحني أن أفعله - إعادة كتابة المشروع بالكامل لإعادته ، أو عدم تحديث رد فعل d.ts مطلقًا أو الاحتفاظ بنسخة متشعبة ودعمها دائمًا؟

mweststrate ، يرجى التحقق من هذا.

Iezious أنا أقدر موقفك وأطلب منك أن تهدأ. أحاول العمل معك. هذا لا علاقة له بـ Redux ، بل علاقة بـ React النقي.

لا أريد أن يحظر DT حالة استخدام نجحت سابقًا ، ولكن لا أعتقد أن الطريقة التي تصف بها استخدام mobx مع رد فعل تتوافق مع وثائق التفاعل (ولا حتى وثائق mobx التي قرأتها الآن هو - هي).

تشير React بوضوح في الوثائق إلى أن هناك ثلاث طرق لاستخدام الحالة بشكل صحيح ، وأولها هو "عدم تعديل الحالة بشكل مباشر".

يقودني هذا إلى الاعتقاد بأن الطريقة التي تعمل بها قاعدة التعليمات البرمجية الخاصة بك حاليًا من المحتمل جدًا أن تتعطل في الإصدارات المستقبلية من التفاعل. أثناء البحث في https://github.com/mobxjs/mobx-react ، لا أرى أي اقتراح بأنك تستخدم الحالة بهذه الطريقة. في الواقع ، يبدو أنهم يريدون منك استخدام الخصائص.

بمراجعة https://mobx.js.org/getting-started.html و googling لـ "mobx reaction state" ، أخفق في العثور على أي وثائق تقترح استخدام mobx بالطريقة التي تفعل بها ذلك.

من المفترض أن تنقل DT روح المكتبة الأساسية في أحسن الأحوال والتنفيذ الفعلي في أسوأ الأحوال ومن الواضح أن الشراء في عنصر التفاعل والتمديد يعني احترام العقد الضمني.

لست متأكدًا مما أقترح عليك فعله. بعض الخيارات التي يمكنني التفكير فيها:

  1. خيار "رخيص" ، إذا كنت تصر على تولي المتغير state هو البحث عن React.Component واستبداله بـ MyComponent وتحديد MyComponent ليكون فئة فرعية من React.Component بدون قيود القراءة فقط.
  2. آخر ، استنادًا إلى الأمثلة الاصطلاحية المنشورة في وثائق mobx ، هو أخذ الوقت الكافي للتوقف عن استخدام this.state واستخدام المتغيرات على React.Component الفعلي. قد يكون هذا مؤلمًا بعض الشيء ولكن على الأقل سيتمكن الأشخاص الجدد في مشروعك من رؤية الأنماط في قاعدة التعليمات البرمجية الخاصة بك كما هو موصوف على الإنترنت.
  3. يمكنك إعادة إعلان state في كل مكون إذا كنت تريد الاستمرار في القيام بذلك على غرار ما تفعله.
  4. يمكنك البحث عن this.state واستبداله بـ this.somethingElse والإعلان يدويًا.
  5. يمكنك التوقف عن تلقي التحديثات للرد من DT (وربما في المستقبل من الرد بشكل عام اعتمادًا على كيفية تأثير التغييرات المستقبلية على حالة الاستخدام الخاصة بك).

إذا كان هذا هو مشروعي ، فمن المحتمل أن أفعل رقم 2 ، على الرغم من أنني لا أعرف ما يكفي عن mobx لأعرفه على وجه اليقين.

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

سأذكر مرة أخرى استراتيجيتنا ، وهي تطبيق مخصص لملاحظات RxJs لدفع تغييرات حالة React وتقديمها والتي تشبه إلى حد كبير نمط mobx. نستخدم الإجراءات لتلقي المدخلات من طبقة العرض (React). الإجراءات مرادفة للدالة التي تستهلك المدخلات وتنتج عنصرًا يمكن ملاحظته ، مما سيؤدي بعد ذلك إلى دفع مراقبات الحالة الأخرى. يسمح هذا النمط للحالة بالبقاء غير قابلة للتغيير من منظور طبقة React ، حيث إنك تقوم فقط بالاستعلام عن القيم التي يمكن ملاحظتها وتنفيذ إجراءات الحالة. داخليًا ، قد "تتحول" دولتك إلى نفسها نتيجة للإجراءات ، حيث لا توجد قيود للقراءة فقط على الحالة الداخلية.

لا أستطيع أن أهدأ. لديّ المشروع قيد الإنتاج وأحتاج إلى قضاء قدر كبير من الوقت مع الفرق ، وهذا يعني المال منذ هذا التغيير.

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

ما هو الهدف من مشروع DT؟ لوصف مكتبات JS بأنها دقيقة بقدر الإمكان ، أو وصف رؤيتنا لطريقة الاستخدام الصحيح لتلك المكتبات؟ وفقًا لاسم "بالتأكيد مكتوب" ، فهو الأول. لذلك ، إذا لم يكن هذا التقييد موجودًا في مكتبة JS الأصلية ، فيجب ألا يكون موجودًا في DT أيضًا. هذه وجهة نظري. أين أنا مخطئ في هذه النقطة؟

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

أرني أونصة واحدة من الوثائق التي وضعها فريق التفاعل والتي تقترح أن الحالة يجب أن تكون قابلة للتغيير بشكل مباشر وسأغير الكود على الفور.

https://facebook.github.io/react/docs/react-component.html#state

لا تقم أبدًا بتغيير this.state مباشرةً ، لأن استدعاء setState () بعد ذلك قد يحل محل الطفرة التي أجريتها. تعامل مع هذه الحالة كما لو كانت غير قابلة للتغيير.

يبدو من الواضح تمامًا أن التفاعل يعتبر this.state غير قابل للتغيير. لا تعتبر React أن الخصائص _ الخاصة بـ this.state غير قابلة للتغيير (وهو افتراض الإعادة). أنت حر في القيام بما يلي:

this.state.user.name = "foo";

في رد فعل اصطلاحي.

أفضّل كتابة واجهة برمجة التطبيقات بدقة (وهو ما يعني في هذه الحالة Readonly ) والتعبير عن جميع الثوابت التي ينص عليها فريق التفاعل.

ericanderson آسف لقد لاحظت هذا للتو. FWIW أعتقد أن التغيير معقول وأن الأدوات ستلحق بالركب. هل سمعت أنهم يفكرون في إهمال الحمولة الزائدة setState التي تأخذ شيئًا ما؟ المستقبل هو نمط التخفيض setState بكل الحسابات.

amoreland لا أوافق. حسب: https://facebook.github.io/react/docs/state-and-lifecycle.html#do -not-edit-state-مباشرة

لا تعدل الحالة مباشرة

على سبيل المثال ، لن يؤدي هذا إلى إعادة تصيير أحد المكونات:

// Wrong
this.state.comment = 'Hello';

بدلاً من ذلك ، استخدم setState ():

// Correct
this.setState({comment: 'Hello'});

المكان الوحيد حيث يمكنك تعيين this.state هو المنشئ.

johnnyreilly لم أفعل. ذلك مثير للاهتمام. مصدر؟

تمت تغطيتها في أحد المحادثات في مؤتمر رد الفعل الأخير. إنه متاح على موقع يوتيوب. ربما كان حديث لين كلارك. إحدى المحادثات الأولى في اليوم الأول - تغطي التغييرات القادمة للتفاعل مع الإصدار 16. الألياف وما إلى ذلك

آسف gaearon قصدته

السبب وراء إجراء mobx لتغييرات الحالة ، هو أن الملاحظات تقود تحديثات React ، بدلاً من _ استبدال_ الحالة تمامًا ، تصبح الحالة محركًا يقود العرض. لذلك من خلال القيام بشيء مثل this.state.updating = true; ، فأنت تقوم في الواقع بعمل مكافئ لاستبدال الحالة ، ولكن بدلاً من ذلك تكون الحالة ذكية بما يكفي لتشغيل عرض جديد عن طريق إخطار المكون بأن الحالة قد تم تغييرها عن محتواها السابق. أوافق على أن هذا ليس استخدامًا _ تقليديًا_ لتفاعل React ، ولكنه استخدام أكثر شمولاً وأعلى مستوى لـ React. أود أن أزعم أن استخدام React التقليدي مفيد فقط للمشاريع الصغيرة التي تحتوي على عدد قليل من المكونات. عندما ينتهي بك الأمر بمئات من المكونات لكل منها العشرات من محركات الطفرات التفاعلية ، لم يعد بإمكانك استخدام تعديلات حالة React التقليدية (على سبيل المثال ، setState) بشكل موثوق به ويجب أن تستمتع بالتغييرات المعمارية التي تتضمن _smart_ state (وهو ما يفعله mobx أساسًا ).

بعد قولي هذا ، أفهم سبب استيائه ، لأن تغييرات الكتابة تؤثر على استخدام أكثر تقدمًا لنظام React. إن تخصيصات الحالة التي يمكن ملاحظتها ليست حالة _mutating_ تقنيًا ، ولكنها تستدعي أحداثًا يمكن ملاحظتها لقيمة RHS. يحدث فقط أن التركيب اللغوي الذي اختاره mobx لإصدار هذه الأحداث التي يمكن ملاحظتها يتعارض مع النية الصريحة لحالة React الثابتة.

Iezious إذا كنت بحاجة إلى حل سريع لهذا النوع من المشاكل ، يمكنك أن تفلت منه من خلال زيادة كتابة React وإعادة تشكيل مكوناتك لاستخدام امتداد إلى React.Component type defs.

import * as React from 'react';
declare module 'react' {
  class MutableStateComponent<P, S> extends React.Component<P, S> {
    state: S;
  }
}
(React as any).MutableStateComponent = React.Component;

والآن يمكنك فقط إنشاء مكونات حالة قابلة للتغيير تمامًا مثل المكونات العادية ، باستثناء أن العضو state لم يعد محددًا على أنه readonly .

export class MyComponent extends React.MutableStateComponent<MyProps, MyState> {
  // this.state.name is writable
}

patsissons نعم ، هذا هو السبب بالضبط.

أنا لا أقوم بتغيير الحالة ، فأنا أستخدم موبكس ​​المرصدات ، والتي تستدعي setState بالنسبة لي ، وأنا أفعل ذلك لأن كود مشروعي الضخم يبدو أكثر وضوحًا ومفهومًا.

أعلم أنه يمكنني إنشاء المكون الخاص بي ، ويمكنني أيضًا في خادم npm إنشاء برنامج نصي سيعيد هذا التغيير دائمًا لشركتنا. يمكنني استخدام الاختراق مثل "this.state.state.updated" والكثير من الاختراقات الأخرى.

أريد فقط أن أقول ، أن هذا التغيير يؤثر على استخدام أنماط يمكن ملاحظتها مثل mobx ، والتي تتبع في الواقع طريقة التفاعل ، ولكن الآن ، نظرًا لأن هذا التغيير لا يمكن تجميعه ويحتاج إلى بعض الاختراقات والحلول للعمل. ولهذا أعتقد أن هذا التغيير ليس صحيحًا.

ربما بدلاً من MutableStateComponent الذي اقترحناه ، نطلق عليه بدلاً من ذلك ObservableComponent وهو أكثر توافقًا مع نمط التفاعل القابل للملاحظة.

إذا قمت بإسقاط Readonly ، فإن الأشخاص الذين يستخدمون أنواع التفاعل مع رد الفعل المنتظم (و / أو أي عدد من أنظمة إدارة الحالة الأخرى بخلاف mobx) يتعرضون للأخطاء. لا أستخدم mobx في مشروعي الضخم وأنا أقدر أخطاء المترجم عندما يقوم شخص ما بخطأ إملائي ويستخدم عن طريق الخطأ this.state.foo = bar .

إذا كانت هناك مقايضة لا مفر منها بين التفاعل القياسي واستخدام التفاعل غير القياسي ، فيجب أن تميل أنواع التفاعل القياسية إلى الأول.

علاوة على ذلك ، إذا كنت تستخدم mobx بالطريقة الاصطلاحية ، فهذه ليست مشكلة.

إذا قمت بإسقاط Readonly ، فإن الأشخاص الذين يستخدمون أنواع التفاعل مع رد فعل منتظم (و / أو أي عدد من أنظمة إدارة الحالة الأخرى بخلاف mobx) يتعرضون للأخطاء. لا أستخدم mobx في مشروعي الضخم وأنا أقدر أخطاء المترجم عندما يقوم شخص ما بخطأ إملائي ويستخدم هذا الخطأ.

لذا مرة أخرى - إنك تُعلم لكتابة المدونة

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

هذا كل شئ.

تضمين التغريدة

إذا كنت بحاجة إلى حل سريع لهذا النوع من المشاكل ، يمكنك أن تفلت منه نوعًا ما عن طريق زيادة كتابة React وإعادة تشكيل مكوناتك لاستخدام امتداد إلى React.Component type defs.

ليس صحيحا. في عالم المؤسسات ، حيث أنا ، لا توجد "حلول سريعة". عندما يتغير شيء ما في SDK ، يجب أن يكون متوافقًا مع الإصدارات السابقة ، أو يستغرق الأمر سنوات. لا توجد إصلاحات سريعة في 2M + سطر من مشروع الكود. لقد مرت أسابيع من التغييرات ، والاختبارات ، واختبار A / B prod ، وطرح عدد كبير من الأشخاص. يكلف نقودًا حقيقية. وكل هذا الجهد الهائل لمجرد أن شخصًا ما أضاف تغييرًا غير متوافق مع الإصدارات السابقة والذي لا يوجد في المكتبة الحقيقية؟

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

كتب ericanderson ذلك

this.state.state.value = 1 

ليس شرعيًا أيضًا من وجهة نظره. في المرة القادمة سيحصل على الأداة من TS ويضيف القيد الذي سيمنع هذا الرمز. أو اجعل فئة المكون النهائية ، أو أي شيء آخر لمجرد "أنه أسلوب صحيح ويمنع الناس من ارتكاب الأخطاء".

منع الأشخاص من التخفيف - إنها مهمة FB ، إذا أرادوا القيام بذلك ، فيمكنهم بسهولة إضافة وكيل وعدم السماح بطفرة الحالة. الغرض من DT هو إضافة الأوصاف ، ولا شيء غير ذلك.

تضمين التغريدة

أعتقد أن الهدف هو أن فريق React لا يمكنه جعل الحالة غير قابلة للتغيير باستخدام JavaScript ، ولكن إذا أمكنهم فعل ذلك. من ناحية أخرى ، يمكن أن تجعل TypeScript الحالة غير قابلة للتغيير ، ولهذا السبب تم إجراء هذا التغيير على تعريفات النوع. كان قصد فريق React مطلقًا عدم السماح بإجراء تعديلات على أعضاء الدولة بشكل مباشر (كما يتضح من مستنداتهم الرسمية حول استخدام الحالة بشكل صحيح ) ، لكن لم يكن لديهم التركيبات اللغوية لفرض هذا القيد. لم يكن هذا القيد مجهولًا أبدًا ، فقد تم توثيقه جيدًا منذ البداية. بالنسبة لأولئك منا الذين يعملون خارج الاستخدام _ التقليدي_ لـ React ، يجب علينا على الأقل الالتزام بتوصيات الاستخدام الرسمية لـ React. بالنسبة لفريقي ، كان هذا يعني تصميم حل يسمح لنا بتحريك تغييرات الحالة دون تغيير الحالة بشكل مباشر. تم إجراء ذلك على وجه التحديد للتأكد من أننا لن نواجه أي مشكلات مثل هذه (على الرغم من أن هذا التغيير قد أثر علينا قليلاً ، ولكن فقط من خلال التوقيعات الوظيفية وليس التصميم الأساسي).

إذا لم تكن إعادة البناء ممكنة في حالتك ، فإما أن تقوم بإدخال دبوس @types/react عند 15.0.1 قبل إجراء التغيير ، أو لا تستخدم @types/react وبدلاً من ذلك احتفظ بالمتغير الخاص بك من اكتب defs وقم ببساطة بتغيير كتابة state مقابل Component . لا أعتقد حقًا أنك ستنجح في إقناع أي شخص بالعودة إلى التغيير.

forceUpdate موجود لأنه موثق كمسار الكود الموصى به لقيادة العرض عند تعديل الحالة الهيكلية الداخلية (أو عندما يستخدم render() بيانات خارج الحالة قابلة للتغيير). تم تصميم استخدام forceUpdate لنمط الاستخدام الذي يستخدمه فريقي بالضبط مع React.

يمكن لفريق React أن يجعل الحالة غير قابلة للتغيير باستخدام JS ، إنه أمر سهل. لكنها غير متوافقة مع الإصدارات السابقة.

مرة أخرى ، هو:

this.state.state.value = 1 

شرعي أم لا؟

أعتقد أنه كذلك ، لكن رأيي واضح بالفعل ...

لا أعتقد أن المحادثة حول القابلية للتغيير / الثبات ذات صلة _ حتى الآن_. من الواضح أن الخطأ في مترجم TS (بخصوص Readonly ) مع هذا التغيير يجعل المكونات العامة مستحيلة تمامًا للاستخدام ، مما يكسر الكثير من الكود الموجود. بالتأكيد هذه حالة استخدام صالحة ومقبولة عالميًا وهي سبب كاف للتراجع عنها في الوقت الحالي ؟؟؟

interface test1 {
    num: number;
}

function add<T extends test1>(initial: T, add: number) {
    var copy: Readonly<T> = initial;

    //ERROR HERE: [ts] Operator '+' cannot be applied to types 'T["num"]' and 'number'.
    return copy.num + add;
}

هل يعرف أي شخص ما إذا كانت هناك مشكلة مفتوحة مع فريق Typescript حول هذا الأمر؟ لا يمكنني العثور على المشكلة ذات الصلة على تعقبهم.

caesay راجع Microsoft / TypeScript # 15501

يجب أن أبدأ الحالة في المُنشئ ، لكن يُظهر tslint "الحالة للقراءة فقط" ....

constructor(props) {
  super(props);
  this.state = {
     value: props.defaultValue,
  };
}

كيف يمكنني إصلاح ذلك...............

استخدم setState

لا يعمل setState في المُنشئ

أو ضع في الاعتبار componentWillMount w / setState

شكرا

أهلا،

قرأت الموضوع بالكامل ولكن ليس من الواضح بالنسبة لي ما إذا كانت هناك خطط للتعامل مع سيناريو @ alanwei0 ؟

أوافق تمامًا على أنه من المنطقي أن يكون لديك state كـ ReadOnly . ومع ذلك ، فإن عدم القدرة على ضبط الحالة الأولية في المُنشئ يعقد الأمور بعض الشيء لأن render يُستدعى قبل أن يتم componentDidMount .

pawelpabich استخدام this.state = { في المنشئ ليس مشكلة. يمكنك تعيين متغيرات readonly في المنشئ ، و Readonly<T> لا يمنع التعيين (على الإطلاق!).

interface TInterface {
    test: string;
}

class TClass {
    readonly blah: Readonly<TInterface>;
    constructor() {
        this.blah = { test: "constructor" };
    }

    fn = () => {
        this.blah = { test: "fn" };
    }
}

الخطأ الوحيد هنا سيكون داخل fn - ليس بسبب Readonly<T> ، ولكن بسبب readonly الكلمة الأساسية. في الواقع ، لا تستخدم أحدث نسخة من الكتابات حتى الكلمة الأساسية readonly ، لذلك يمكنك في الواقع تعيين الحالة في أي مكان ، فقط لا تغير الخصائص داخل الحالة.

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

caesay آسف ، اعتقدت أن هذا هو الحال الذي نتحدث عنه هنا. مشكلتي هي أنني لا أستطيع تعيين الحالة في الفئة الأساسية. أنا مشترك في TS 2.4.1. هل لديك أي فرصة معرّف المشكلة حتى أتمكن من التحقق من ذلك؟

يمكنك دائمًا استدعاء setState (في componentWillMount).

pawelpabich مرة أخرى ، هذه ليست مشكلة :) لا يمكنك تعيين الحالة من الفئة الأساسية عن قصد . كيف استطعت؟ لا تعرف عقد الخاصية الذي يستخدمه المكون المشتق.

interface BaseCompState {
    baseState1?: string;
}

class BaseComp<TState extends BaseCompState> extends React.Component<any, TState> {
    constructor(props) {
        super(props);
        this.state = {
            baseState1: "fromBase",
        };
    }
}

interface TopCompState extends BaseCompState {
    topState1?: string;
}

class TopComp extends BaseComp<TopCompState> {
    constructor(props) {
        super(props);
        this.state = {
            baseState1: "fromTop",
            topState1: "fromTop",
        };
    }
}

هذا مثال بسيط لمكوِّن مشتق (الدعائم محذوفة ، لكن الفكرة نفسها). من الواضح أن this.state = في الفئة الأساسية لا يعمل ، لأنه لا يعرف ما هو TState . علاوة على ذلك ، إذا نجح الأمر ، فسيكون مجرد الإفراط في كتابة الحالة التي حددها الوالد. ستكون الحالة النهائية { baseState1: "fronBase" } . ماذا حدث لممتلكات TopState؟

إذا كنت مقتنعًا تمامًا أنك بحاجة إلى التعامل مع الحالة في المكون الأساسي الخاص بك ، فيمكنك تمرير الحالة من المكون المشتق إلى مُنشئ المكون الأساسي (مثل TState حتى تتمكن من تعيينه) - قد يبدو ذلك مثل هذه:

interface BaseCompState {
    baseState1?: string;
}

class BaseComp<TState extends BaseCompState> extends React.Component<any, TState> {
    constructor(props, state: TState) {
        super(props);
        this.state = Object.assign({
            baseState1: "fromTop",
        }, state);
    }
}

interface TopCompState extends BaseCompState {
    topState1?: string;
}

class TopComp extends BaseComp<TopCompState> {
    constructor(props) {
        super(props, {
            topState1: "fromTop",
        });
    }
}

أو حتى أبسط من ذلك ، يمكنك الاتصال بـ this.setState( من داخل المكون الأساسي الخاص بك (نعم ، يمكنك القيام بذلك في المُنشئ!)

لا توجد مشكلة هنا.

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

import * as React from "react";

/* tslint:disable:max-classes-per-file*/

interface BaseProps {
    baseProp: string;
}

interface BaseState {
    baseState: string;
}

class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
    constructor(props) {
        super(props);

        this.state = {
            baseState: props.baseProp
        };
    }

    render() {
        return <div>{this.state.baseState}</div>;
    }
}

interface DerivedProps extends BaseProps {
    derivedProp: string;
}

interface DerivedState extends BaseState {
    derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
    constructor(props) {
        super(props);

        this.state = {
            derivedState: props.derivedProp
        };
    }

    render() {
        return <div>{this.state.derivedState}</div>;
    }
}

أخطاء

webpack: Compiled successfully.
ERROR at Test.tsx(17,9):
TS2322: Type '{ baseState: any; }' is not assignable to type 'Readonly<TState>'.

ERROR at Test.tsx(39,9):
TS2322: Type '{ derivedState: any; }' is not assignable to type 'Readonly<DerivedState>'.
  Property 'baseState' is missing in type '{ derivedState: any; }'.

Version: typescript 2.4.1

أولا. لم تتم كتابة الدعائم في القاعدة في المُنشئ. وبالتالي فإن props.baseProp هو any ، وهو غير قابل للتخصيص!

ثانيًا ، تحتوي أدواتك في Derived على المشكلة نفسها وتفتقد baseState . الذي بالطبع لن يعمل ، بغض النظر عن Readonly

TProps extends BaseProps مما يعني أن TProps لديه على الأقل نفس الأعضاء الذين BaseProps . فكيف لم يتم تعريف ذلك؟ أنا أفهم أن المترجم قد لا يكون قادرًا على الاستدلال عليه ولكن القول بأن ذلك لم يتم تعريفه لا يبدو صحيحًا. يمكن تطبيق نفس التفكير على Derived .

caesay setState في المُنشئ ليس حلاً موثوقًا به لأن هذه الطريقة غير متزامنة ولا يزال بإمكانك الوصول إلى render بدون تعيين الحالة الأولية.

الحل الوحيد الموثوق الذي يمكنني رؤيته هو ضبط الحالة بأكملها في الفئات المشتقة. هذا له عيب واضح وهو:

  1. هذا يحتاج إلى أن يتكرر في كل فئة مشتقة
  2. تحتاج الفصول المشتقة إلى معرفة الحالة التي لا يهتمون بها.

المثال الذي عرضته أعلاه سيعمل في C # لذا سيكون من الجيد أن يعمل في TypeScript.

  1. ~ setState آمنة في الباني ~
  2. في حالة الفئة المشتقة ، فإن أفضل حالة يمكنني رؤيتها هي استدعاء setState في componentWillMount . قاعدتك لا تعرف ما يجب أن يكون في الحالة لطفلها ، لذلك لا يمكنها الحصول على this.state في تكوين آمن. ومع ذلك ، يمكن للفئة الفرعية الخاصة بك استدعاء componentWillMount للوالد ثم تعيين الحالة التي تعتقد أنها بحاجة إليها أيضًا.
  3. هناك ميزات لغوية في العديد من اللغات سيكون من الجيد وجودها في الكتابة المطبوعة. بعضها ممكن. البعض الآخر ليس كذلك. في كلتا الحالتين ، فهي ليست حجة لإدراجها.
  4. الأخطاء التي تراها منطقية. لا يقترحون خطأ في الكتابة المطبوعة ولا في الكتابة. في كل حالة ، تحاول حرفياً تخصيص this.state بكائن لا يتطابق مع النوع المتوقع.

محرر ، ليعكس أنني كنت مخطئًا بشأن setState في المُنشئ ولإضافة backticks لسهولة القراءة.

ericanderson لا أعتقد أننا نحرز أي تقدم هنا. لقد عرضت لك مثالاً وسأكون ممتنًا لو تمكنا من التركيز عليه. وإلا فإنه من الصعب إجراء مناقشة.

Re setState ليس آمنًا للاستخدام في المُنشئ. لا يتسبب في حدوث خطأ ولكنه لن يقوم بتعيين الحالة قبل حدوث التصيير. لدي شفرة تعطلت بسبب ذلك ومستندات React واضحة جدًا ولا يجب استخدامها هناك.

pawelpabich لا ، هذا ليس المكان المناسب لهذه الحجة. أنت مخطئ بشكل أساسي في فهمك للغة. في المثال الذي قدمته ، أنت غير راضٍ عن عقد "الولاية" في أيٍّ من مهامك للدولة.

على سبيل المثال ، عندما تفعل

this.state = { baseState: props.baseProp };
// now the state is exactly { baseState: props.baseProp }

الولاية هي بالضبط { baseState: props.baseProp } وهذا لا يفي بمتطلبات TProps extends BaseProps (لأننا لا نعرف ما هي TProps! يمكن أن تحتوي على أي خصائص فيها).

بعد ذلك ، تقوم بمهمة _ منفصلة _.

this.state = { derivedState: props.derivedProp };
// now the state is exactly {  derivedState: props.derivedProp }

الولاية الآن هي بالضبط { derivedState: props.derivedProp } (لقد قمت بالكتابة فوق تعيين الحالة الأساسية الخاص بك !!) وهذا لا يرضي DerivedState OR BaseProps.

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

كملاحظة جانبية - أنت تتجاوز أيضًا طريقة الأساس render() ، مما يعني أن المكون الأساسي الخاص بك لن يكون قادرًا على تقديم أي شيء. إذا كنت مقتنعًا بأن هذا هو ما تريده ، فيمكنك إضافة عضو محمي getBaseState() واستدعاء هذا من المُنشئ المشتق عند تعيين الحالة (حتى لا تضطر إلى تكرار منطق الحالة الأساسية). لكن ما أعتقد أنك تريده حقًا هو عدم استخدام المكونات المشتقة على الإطلاق. حاول إعادة الهيكلة لاستخدام التركيب (حيث يكون لديك كائن واحد يحتوي على كائنات فرعية متعددة). أعتقد أنك ستجده سيصبح أكثر نظافة.

لا أريد الابتعاد عن القراءة لإنشاء مشروع جديد لمجرد مناقشة هذا معك ، ولكن للأسف ...

سأقف مصححًا حول setState () في المُنشئ ، لكن هذا لا يغير شعوري حيال استخدامه في componentWillMount .

مثال عملي لكيفية القيام بذلك:
https://github.com/ericanderson/set-state-example

على وجه التحديد ، index.tsx:

import * as React from "react";
import * as ReactDOM from "react-dom";

/* tslint:disable:max-classes-per-file*/

interface BaseProps {
  baseProp: string;
}

interface BaseState {
  baseState: string;
}

class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
  public componentWillMount() {
    this.setState({
      baseState: this.props.baseProp,
    });
  }

  public render() {
    return (
      <p>
        <code>this.state.baseState: </code>
        {this.state.baseState}
      </p>
    );
  }
}

interface DerivedProps extends BaseProps {
  derivedProp: string;
}

interface DerivedState extends BaseState {
  derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
  public componentWillMount() {
    super.componentWillMount();
    this.setState({
      derivedState: this.props.derivedProp,
    });
  }

  public render() {
    return (
      <div>
        <p>
          <code>this.state.derivedState: </code>
          {this.state.derivedState}
        </p>
        {super.render()}
      </div>
    );
  }
}

ReactDOM.render(<Derived derivedProp="its derived" baseProp="its basic" />, document.getElementById("main"));

pawelpabich إذا كنت تريد تنفيذ مكون متعدد الأشكال مع حالة أولية ، فستحتاج إلى جعل المكون الأساسي مجردة وإنشاء دالة مجردة getInitialState() (أو ذات موضوع مشابه) لتنفيذه في الفئة المشتقة. ما عليك سوى تعيين الحالة مرة واحدة ، إما في المُنشئ أو باستخدام setState كما أظهرericanderson .

فيما يلي مثالك الذي تم تحويله إلى حل متعدد الأشكال بالكامل ، مع فصل كامل للمخاوف فيما يتعلق ببناء الدولة:

interface BaseProps {
  baseProp: string;
}

interface BaseState {
  baseState: string;
}

abstract class Base<TProps extends BaseProps, TState extends BaseState> extends React.Component<TProps, TState> {
  constructor(props: TProps) {
      super(props);

      this.state = this.getInitialState();
  }

  protected abstract getInitialState(): TState;

  protected getBaseState() {
    return this.props.baseProp;
  }

  render() {
      return <div>{this.state.baseState}</div>;
  }
}

interface DerivedProps extends BaseProps {
  derivedProp: string;
}

interface DerivedState extends BaseState {
  derivedState: string;
}

export class Derived extends Base<DerivedProps, DerivedState> {
  getInitialState(): DerivedState {
    return {
      baseState: this.getBaseState(),
      derivedState: this.props.derivedProp,
    };
  }

  render() {
      return <div>{this.state.derivedState}</div>;
  }
}

تضمين التغريدة

caesay أعترف أنني كنت مخطئًا ولسبب ما لم أر أن التعيينات ستتجاوز بعضها البعض. ومع ذلك ، فإن استخدام CAPS و ! لم يساعدني في الخروج من الحفرة.

ركز patsissons و ericanderson على المشكلة والآن لدينا حل يمكن للآخرين استخدامه.

pawelpabich أوافق على أن سلوكياتي كانت أقل من احترافية - لكن من المفهوم لذلك بالنظر إلى أنني قدمت لك العديد من التفسيرات والعينات وما إلى ذلك ، واخترت عدم الاستماع إلي.

عندها سيكون الأمر مجرد كتابة مفرطة للحالة التي حددها الوالد.

[_إذا كنت تريد_] معالجة الحالة في المكون الأساسي ، فيمكنك تمرير الحالة من المكون المشتق إلى مُنشئ المكون الأساسي

[_ إذا كنت تريد معالجة الحالة في المكون المشتق الخاص بك_] يمكنك إضافة عضو محمي getBaseState () واستدعاء هذا من المُنشئ المشتق عند تعيين الحالة.

ما فعله patsissons هو أخذ التعليقات التي سبق ذكرها هنا وتقديم عينة رمز - وهو ما لا ينبغي أن يكون ضروريًا. هذا ليس تدفقًا مكدسًا ، ولا نقدم غالبًا عينات أكواد جاهزة هناك أيضًا.

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

import * as React from 'react';

export default class HomePage extends React.Component<any, Map<string, string>> {

  public componentWillMount() {
    const map = new Map<string, string>();
    map.set('aKey', 'aValue');
    this.setState(map);
  }

  public render() {

      return (
        <div className="home">
          <div className="greeting">
            Home page: {this.state.get('aKey')} // <-- I get an error here
          </div>
        </div>
      );
  }
}

الخطأ:

homePage.tsx:12 Uncaught TypeError: this.state.get is not a function
    at HomePage.render (homePage.tsx:12)
    at eval (ReactCompositeComponent.js:793)
    at measureLifeCyclePerf (ReactCompositeComponent.js:73)
    at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (

يجب أن تكون الحالة دائمًا عبارة عن كائن ذي مفتاح عادي afaik ، لذلك بدلاً من ذلك قم بتعريف الحالة
كشيء مثل: { values: Map <string, string> } وقراءته
this.state.values.get('aKey')

المرجع vr 29 سبتمبر. 2017 09:01 schreef Janusz Białobrzewski <
[email protected]>:

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

استيراد * كرد فعل من "رد فعل" ؛
يقوم HomePage بتصدير الفئة الافتراضية لتوسيع React.Component> {

public componentWillMount () {
خريطة const = خريطة جديدة() ؛
map.set ('aKey'، 'aValue')؛
this.setState (خريطة) ؛
}

عرض عام () {

  return (
    <div className="home">
      <div className="greeting">
        Home page: {this.state.get('aKey')} // <-- I get an error here
      </div>
    </div>
  );

}
}

الخطأ:

الصفحة الرئيسية. tsx: 12 خطأ في النوع غير معلوم: this.state.get ليست دالة
في HomePage.render (homePage.tsx: 12)
في EVAL (ReactCompositeComponent.js: 793)
at scaleLifeCyclePerf (ReactCompositeComponent.js: 73)
في ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/14250#issuecomment-333047367 ،
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/ABvGhM5hDyRNyUeZuIiGeTZk1N-rfuA4ks5snJW5gaJpZM4LuDWV
.

شكرًا ، ولكن يبدو أنه جهد لا طائل منه لإعلان الحالة على أنها Readonly<S> ، لأن أعضائها المتداخلين لا يتأثرون بالقراءة فقط.

من المحتمل أن يتم تطبيق Readonly يومًا ما بشكل متكرر ، ولكن في الوقت الحالي ، عليك التأكد من أنك تتعامل معه بشكل صحيح. في حالتك ، يجب أن تعلن حقًا عن ReadonlyMap أو

interface State {
    readonly [key: string]: string;
}

أو متداخل:

interface State {
    map: { readonly [key: string]: string };
}

يمكننا استخدامه للقراءة العميقة فقط:

export type DeepReadonly<T> =
  T extends Array<any> ?
  ReadonlyArray<T[0]> :
  T extends Date ?
  T :
  T extends Function ?
  T :
  T extends object ?
  { readonly [P in keyof T]: DeepReadonly<T[P]> } :
  T;

export type Writable<T> =
  T extends ReadonlyArray<any> ?
  Array<WritableObject<T[0]>> :
  T extends Array<any> ?
  Array<WritableObject<T[0]>> :
  WritableObject<T>;

type WritableObject<T> =
  T extends Date ?
  T :
  T extends Function ?
  T :
  T extends object ?
  { -readonly [P in keyof T]: Writable<T[P]> } :
  T;
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

JudeAlquiza picture JudeAlquiza  ·  3تعليقات

csharpner picture csharpner  ·  3تعليقات

jbreckmckye picture jbreckmckye  ·  3تعليقات

demisx picture demisx  ·  3تعليقات

variousauthors picture variousauthors  ·  3تعليقات