React: تنفيذ تحميل البيانات الجانبية

تم إنشاؤها على ١٣ مارس ٢٠١٥  ·  136تعليقات  ·  مصدر: facebook/react

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

type RecordOfObservables = { [key:string]: Observable<mixed> };

class Foo {

  observe(): RecordOfObservables {
    return {
      myContent: xhr(this.props.url)
    };
  }

  render() {
    var myContent : ?string = this.data.myContent;
    return <div>{myContent}</div>;
  }

}

يتم تنفيذ الملاحظة () بعد componentWillMount / componentWillUpdate ولكن قبل التصيير.

لكل مفتاح / قيمة في السجل. اشترك في الملحوظة في القيمة.

subscription = observable.subscribe({ onNext: handleNext });

نسمح باستدعاء onNext بشكل متزامن من الاشتراك. إذا كان الأمر كذلك ، فإننا نضع:

this.data[key] = nextValue;

وإلا فإننا نتركه على أنه غير محدد للتصيير الأولي. (ربما قمنا بتعيينه على null؟)

ثم تقديم العائدات كالمعتاد.

في كل مرة يتم استدعاء onNext ، نقوم بجدولة "this.data [key]" الجديدة التي تؤدي بشكل فعال إلى فرض تحديث على هذا المكون. إذا كان هذا هو التغيير الوحيد ، فلن تتم إعادة تنفيذ الملاحظة (componentWillUpdate -> render -> componentDidUpdate).

إذا تغيرت الخاصيات / الحالة (أي تحديث من recieveProps أو setState) ، فسيتم إعادة تنفيذ الملاحظة () (أثناء التسوية).

في هذه المرحلة ، نلتف حول الرقم القياسي الجديد ، ونشترك في جميع المرصدات الجديدة.

بعد ذلك ، إلغاء الاشتراك في المرصدات السابقة.

subscription.dispose();

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

عند إلغاء تثبيت أحد المكونات ، نقوم تلقائيًا بإلغاء الاشتراك من جميع الاشتراكات النشطة.

إذا لم يتصل الاشتراك الجديد على الفور بـ onNext ، فسنستمر في استخدام القيمة السابقة.

لذلك إذا تغير this.props.url الخاص بي من المثال الخاص بي ، وأشترك في عنوان URL جديد ، فسيستمر myContent في إظهار محتوى عنوان url السابق حتى يتم تحميل عنوان url التالي بالكامل.

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

قد تكون أفضل ممارسة هي إرسال قيمة "فارغة" على الفور إذا لم يكن لديك ذاكرة تخزين مؤقت للبيانات. هناك بديل آخر ، وهو أن يقدم المرصد كل من عنوان URL (أو المعرف) والمحتوى في النتيجة.

class Foo {

  observe() {
    return {
      user: loadUser(this.props.userID)
    };
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

يجب أن نستخدم عقد RxJS الخاص بـ Observable نظرًا لأن هذا أكثر شيوعًا ويسمح بالتنفيذ المتزامن ، ولكن بمجرد استخدام اقتراح jhusain بشكل أكثر شيوعًا ، سننتقل إلى هذا العقد بدلاً من ذلك.

var subscription = observable.subscribe({ onNext, onError, onCompleted });
subscription.dispose();

يمكننا إضافة المزيد من خطاطيف دورة الحياة التي تستجيب لهذه الأحداث إذا لزم الأمر.

ملاحظة: يسمح هذا المفهوم للبيانات الجانبية بالتصرف مثل "السلوكيات" - تمامًا مثل الدعائم. هذا يعني أنه ليس علينا أن نفرط في حالة الفكرة لهذه الأشياء. يسمح بالتحسينات مثل التخلص من البيانات فقط لإعادة الاشتراك لاحقًا. إنه قابل للترميم.

Component API Big Picture

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

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

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

استعمال:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

ال 136 كومينتر

undefined المحتمل أن يكون data حتى توفر القيمة القابلة للملاحظة أول قيمة لها عبر onNext . على سبيل المثال في Relay ، نقوم بتعيين معاني مختلفة لـ null (البيانات غير موجودة) و undefined (لم يتم جلبها بعد) ، لذلك ستكون قيمة البيانات الافتراضية المثالية هي undefined . البديل هو توفير طريقة جديدة ، على سبيل المثال getInitialData ، لكنني أظن أن هذا غير ضروري / مبالغة.

هذا مثير للاهتمام للغاية ، ولكن من وجهة نظر الكتابة الثابتة ، لست سعيدًا جدًا بنظام المفتاح / القيمة ، فمن المستحيل إلى حد كبير التعبير عن نوعها.
لماذا لا يكون لديك observe يعيد قيمة واحدة يمكن ملاحظتها وتعيين / دمج القيمة التي تم حلها إلى this.data :

class Foo {

  observe() {
    return (
      loadUser(this.props.userID)
        .map(user => { user })
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

وفي حالة الجلب المتعدد ، هناك شيء مثل:

class Foo {

  observe() {
    return (
     combineLatest(
      loadUser(this.props.userID),
      loadSomethingElse(this.props.somethingElseId),
      (user, somethingElse) => ({ user, somethingElse})
     )
  }

  render() {
    ..
  }

}

ربما يكون هذا مطولاً أكثر قليلاً ، لكنه يسمح بالحصول على نوع ثابت لطيف:

interface Comp<T> {
  observe(): Observable<T>;
  data: T;
}

أيضًا بدلاً من إعادة تنفيذ observe عند تغيير الخاصيات / الحالة ، يمكننا الوصول إلى حالة "الخاصيات" باعتبارها حالة يمكن ملاحظتها:

class Foo {

  observe(propsStream) {
    return (
      propsStream
        .flatMap(({ userID }) => loadUser(userId))
        .map(user => { user })
    );
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }
}

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

ومع ذلك ، للاشتراك في متجر Flux بسيط ، لن تحتاج إلى ذلك.

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

class Foo extends React.Component {
  data : { user : User, content : string };
  observe() /* implied as { user : Observable<User>, content : Observable<string> } */ {
  }
}

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

ccericvicenti

على الأقل في حالة الكتابة المطبوعة ، لن تكون هناك طريقة لتقييد نوع الإرجاع observe بناءً على نوع data ، على الأقل حتى شيء مثل https://github.com/ تم تنفيذ

أود استخدام هذا للإصدار التالي من React DnD ، لكن من الواضح أن هذا يتطلب انتظار React 0.14.
أتساءل عما إذا كان بإمكاني "polyfill" هذا في الوقت الحالي باستخدام مكون ذي ترتيب أعلى يقوم بتعيين this.data على مثيل ref .. قد يكون مجنونًا للغاية.

هل يمكن الوفاء بالوعود؟ ثم يمكن للمرء استخدام شجرة الوعود لحل البيانات لشجرة المكونات بالكامل قبل التصيير الأول! سيكون هذا مفيدًا جدًا لـ React من جانب الخادم.

ما هي فوائد جعل هذا API من الدرجة الأولى؟ يمكن تحقيق ذلك بشكل أساسي باستخدام "مكون عالي المستوى".

ما هي فوائد جعل هذا API من الدرجة الأولى؟ يمكن تحقيق ذلك بشكل أساسي باستخدام "مكون عالي المستوى".

يعد الالتفاف في 5 مراكز ترتيب أساسية للحصول على 5 اشتراكات أمرًا صعبًا بعض الشيء ويصعب فهمه للمبتدئين. إن فهم componentWillReceiveProps ليس بالأمر السهل. هذا يصلح كليهما.

أنا ، على سبيل المثال ، أرحب بأسيادنا الجدد الذين يمكن ملاحظتهم.

أتساءل عما إذا كان هذا يمكن أن يساعد في تقريب https://github.com/chenglou/react-state-stream من React's vanilla API

ألن يتطلب الأمر ترتيبًا واحدًا فقط؟ في المثال الموجود في منشورك على الوسيط المتوسط ، يمكنك تكرار ما يزيد عن stores والاشتراك في كل منها.

stores.forEach(store =>
  store.addChangeListener(this.handleStoresChanged)
);

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

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

(أخبرني vjeux أنه يجب أن ذا .)

لا أقصد الترويج لعملي الخاص ، لكنني أعتقد أن هذا الخطاف مشابه جدًا للخطاف getNexusBindings في React Nexus . أنت تعلن عن أقسام البيانات على مستوى المكون عبر خطاف دورة الحياة (والذي يمكن أن يعتمد على الدعائم).

تبدو واجهة برمجة التطبيقات مثل:

class UserDetails {
  getNexusBindings(props) {
    return {
      // binding to data in the datacenter
      posts: [this.getNexus().remote, `users/${this.props.userId}/posts`],
      // binding to data in the local flux
      mySession: [this.getNexus().local, `session`],
    }
  }
}

يتم تطبيق / تحديث الربط خلال componentDidMount و componentWillReceiveProps . في الحالة الأخيرة ، تكون الارتباطات التالية مختلفة عن الارتباطات السابقة ؛ الارتباطات التي تمت إزالتها غير مُشتركة ، والارتباطات المضافة مُشتركة. يتم وصف آلية الجلب / التحديث الأساسية في تنفيذ Nexus Flux . باستخدام نفس واجهة برمجة التطبيقات ، يمكنك إما الاشتراك في البيانات المحلية (المتاجر المحلية التقليدية) أو البيانات البعيدة (الجلب باستخدام GET وتلقي التصحيحات عبر Websockets / polyfill). يمكنك بالفعل الاشتراك في البيانات من نافذة أخرى (باستخدام postWindow) أو WebWorker / ServiceWorker ولكني ما زلت لم أجد حالة استخدام مفيدة حقًا لهذا الغرض.

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

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

aaronshafgaearon فائدة مما يجعل من الدرجة الأولى هي:

1) لا تتآكل في مساحة اسم الدعائم. على سبيل المثال ، لا يحتاج المكون ذو الترتيب الأعلى إلى المطالبة باسم data من كائن الدعائم الذي لا يمكن استخدامه لأي شيء آخر. يؤدي ربط عدة مكونات ذات ترتيب أعلى باستمرار إلى تناول المزيد من الأسماء وعليك الآن إيجاد طريقة للحفاظ على هذه الأسماء فريدة. ماذا لو كنت تؤلف شيئًا قد يكون مؤلفًا بالفعل ولديك الآن تعارض في الاسم؟

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

2) لا يتعين علينا استخدام state لتخزين آخر قيمة. مفهوم data مشابه لمفهوم props بمعنى أنه مجرد ذاكرة. نحن أحرار في التخلص منها في أي وقت إذا احتجنا إلى استعادة الذاكرة. على سبيل المثال ، في التمرير اللانهائي ، قد نقوم تلقائيًا بتنظيف الأشجار الفرعية غير المرئية.

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

أ) لا يمكن إلغاؤها تلقائيا من قبل الإطار. أفضل ما يمكننا فعله هو تجاهل قرار متأخر. في غضون ذلك، يحمل الوعد على الموارد التي يحتمل أن تكون باهظة الثمن. من السهل الدخول إلى حالة من التراجع من الاشتراك / إلغاء / Subscribe / إلغاء ... من طلبات تشغيل / توقيت شبكة طويلة وإذا كنت تستخدم الوعود، فلن يلغي في الجذر وبالتالي عليك أن تنتظر فقط الموارد لإكمالها أو انتهت المهلة. يمكن أن يكون هذا ضارا بالأداء في صفحات سطح المكتب الكبيرة (مثل Facebook.com) أو تطبيقات الكمون الهامة في بيئات تقييد الذاكرة (مثل الرد-الأصلية).

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

لذلك يمكنني أن أجد أن Appleable هو API المتفوق للبناء من حيث لا يقيمك في إصلاح هذه المشكلات إذا كنت بحاجة إلى ذلك.

elierotenberg شكرا على

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

أعتقد أن رد الفعل - nexus يسمح بالتحميل غير المتزامن داخل أحد المكونات قبل الاستمرار في أسفل شجرة العرض.

نعم ، يفصل رد الفعل-nexus صراحة:
1) إعلان ملزم كـ getNexusBindings (وهي طريقة دورة حياة متزامنة وخالية من الآثار الجانبية ، تشبه التصيير - في الواقع كانت تستخدم اسم RenderDependencies ولكنني اعتقدت أنها مربكة) ،
2) اشتراك / تحديث ملزم كـ applyNexusBindings (وهو متزامن ويختلف عن روابط الرابطة السابقة لتحديد الارتباطات الجديدة التي يجب الاشتراك وأيها يجب إلغاء الاشتراك)
3) الجلب المسبق للربط كـ prefetchNexusBindings (وهو غير متزامن ويتم حله عندما تكون القيمة "الأولية" (ما يعنيه هذا) جاهزة)

ReactNexus.prefetchApp(ReactElement) بإرجاع Promise(String html, Object serializableData) . يحاكي هذا الخطاف بناء شجرة React (باستخدام instantiateReactComponent ) ويقوم ببناء / إعداد مسبق / تصيير بشكل متكرر للمكونات. عندما تكون شجرة المكونات بأكملها "جاهزة" ، فإنها تستدعي أخيرًا React.renderToString ، مع العلم أن جميع البيانات جاهزة (أخطاء نمطية). بمجرد حلها ، يمكن إدخال قيمة هذا الوعد في استجابة الخادم. بالنسبة للعميل ، تعمل دورة الحياة العادية React.render() كالمعتاد.

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

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

استعمال:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

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

جانبا لماذا لا تيارات؟ ليس لدي حصان في السباق ، لكني بصراحة أتساءل ؛ هناك بالفعل عمل تم إنجازه على تدفقات الويب ، وبالطبع هناك عقدة

اثنان آخران يجب مراعاتهما: https://github.com/cujojs/most و https://github.com/caolan/highland

jquense هناك عمل نشط على اقتراح لإضافة Observable إلى ECMAScript 7 (+) لذا من الأفضل أن يصبح هذا هو JS عادي. https://github.com/jhusain/asyncgenerator (منتهي الصلاحية حاليًا.)

لن نتعامل مع الاعتماد على RxJS. واجهة برمجة التطبيقات API تافهة لتنفذها بنفسك دون استخدام RxJS. RxJS هي الأقرب إلى اقتراح ECMAScript النشط.

يبدو most.js ممكنًا أيضًا.

يبدو من الصعب استهلاك واجهة برمجة تطبيقات Bacon.js دون الاعتماد على Bacon بسبب استخدام أنواع Bacon.Event لفصل القيم.

دفق APIs عالية المستوى وبعيدة عن حالة الاستخدام هذه.

هل هناك نوع من خيار "انتظار قبل التقديم"؟ أعني أنه ليس من الضروري بالنسبة للعميل انتظار جميع Observables قبل العرض ، ولكن على الخادم ، قد ترغب في انتظار حلها بحيث يكون العرض () لكل مكون كاملة وليست جزئية.

[parent] await observe(). full render(). -> [foreach child] await observe(). full render().

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

بعد هذه المناقشة ، حاولت تلخيص ما يفعله React Nexus في المنشور التالي:

تم تنفيذ تطبيقات Ismorphic بشكل صحيح باستخدام React Nexus

هيريس الرسم التخطيطي لروتين الجلب المسبق الأساسي:

React Nexus

لن نتعامل مع الاعتماد على RxJS. واجهة برمجة التطبيقات API تافهة لتنفذها بنفسك دون استخدام RxJS. RxJS هي الأقرب إلى اقتراح ECMAScript النشط.

: +1: هذا هو مصدر القلق الكبير بالنسبة لي ، التفكير في الوعود حيث يكون تنفيذ وعودك أمرًا محفوفًا بالمخاطر إلا إذا كنت تعرف ما تفعله. أعتقد بخلاف ذلك ستنتهي بمتطلبات ضمنية على lib معين في النظام البيئي. بشكل عرضي ... أحد الأشياء الرائعة من عالم الوعد هو مجموعة اختبار A + ، لذلك حتى عبر المكتبات كان هناك على الأقل ضمان لوظيفة مشتركة .then ، والتي كانت مفيدة للتشغيل المتداخل الوعد قبل أن تكون موحد.

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

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

قد تصبح أكثر تعقيدًا (وأبطأ) إذا أصرت اللجنة على أن استدعاء next يقوم بجدولة مهمة صغيرة مثل الوعود.

قد يزعج ذلك بعض الأنماط التي تستند إلى حقيقة أن onNext متزامن في RxJS: /

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

بهذه الطريقة يمكنك القيام بأشياء مثل: MyStore.get(this.props.someID) واستعادة نفس الشيء الذي يمكن ملاحظته دائمًا.

بهذه الطريقة يمكنك القيام بأشياء مثل: MyStore.get (this.props.someID) واستعادة نفس خاصية Observable دائمًا.

هل استخدام this.props.key (أعلم) سيكون منطقيًا؟ في معظم الحالات ، تقوم بالفعل بتمرير هذا المعرف الفريد باستخدام <... key={child.id} .. /> .

بهذه الطريقة يمكنك القيام بأشياء مثل: MyStore.get (this.props.someID) واستعادة نفس خاصية Observable دائمًا.

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

sebmarkbagegaearon كيف ستلاحظ العمل على الخادم في v0.14؟
هل سيكون قادرًا على انتظار حل جميع المراقبين بشكل صحيح قبل تحويلها إلى سلسلة مشابهة لكيفية تفاعل الارتباط (ولكنها مضمنة للتفاعل)؟

IMO سيكون رائعًا إذا انتظرت المكونات أول قيمة تمت ملاحظتها قبل أن تكون "جاهزة" للعرض على الخادم.

gaearon : سيكون أمرًا رائعًا IMO أن تنتظر المكونات أول قيمة تمت ملاحظتها قبل أن تصبح "جاهزة" للعرض على الخادم.

نعم ،: +1: للعرض غير المتزامن. في هذه الأثناء ، react-async بواسطة andreypopp هو بديل ، لكنه يتطلب fibers لـ "hack" React. سيكون من الرائع لو تمكنت React من دعم التصيير غير المتزامن خارج الصندوق.

العرض غير المتزامن هو شيء نود دعمه ولكنه ليس جزءًا من هذه المشكلة. إل

ربما لن يصل إلى 0.14 لسوء الحظ. هناك حاجة إلى العديد من التصاميم المختلفة للنظر فيها وإعادة بنائها.

لا تتردد في إنشاء وإصدار وصف التغييرات المعمارية على الأجزاء الداخلية اللازمة لتحقيق ذلك.

كان لدي نفس الفكر مثل gaearon re: رد فعل تدفق الدولة . نظرًا لجميع التطبيقات المحتملة بخلاف التحميل الجانبي ، فربما يكون هناك اسم أفضل من data ؟ على سبيل المثال ، فإن observed سيربطها بشكل أكثر وضوحًا بالطريقة.

لا تقصد الخروج عن مساره مع ركوب الدراجات ولكنك أردت التخلص من ذلك.

لا يمكن أن تنتظر ما يمكن ملاحظته في React. هذا يجب أن يجعل React تفاعليًا كما أفهمه

أقوم بتجربة فكرة مماثلة أثناء إعادة كتابة react-async ، راجع README .

يتمثل الاختلاف الملحوظ في أنني أدخلت هوية واضحة / عملية للتوفيق بين العمليات ، على غرار الطريقة التي يعمل بها React مع key prop والمكونات ذات الحالة.

عندما يتغير id للعملية المسماة ، يوقف React Async مثيل العملية القديمة ويبدأ مثيلًا جديدًا.

تبدو واجهة برمجة التطبيقات مثل:

import React from 'react';
import Async from 'react-async';

function defineFetchProcess(url) {
  return {
    id: url,
    start() {
      return fetch(url)
    }
  }
}

function MyComponentProcesses(props) {
  return {
    user: defineFetchProcess(`/api/user?user${props.userID}`)
  }
}

@Async(MyComponentProcesses)
class MyComponent extends React.Component {

  render() {
    let {user} = this.props
    ...
  }
}

تتبع واجهة برمجة التطبيقات للعملية الآن واجهة برمجة تطبيقات ES6 Promises من الناحية التركيبية ومن ناحية الاسم ، ولكن من الناحية المعنوية ، لا يُتوقع أن يتم استدعاء process.then(onNext, onError) مرة واحدة فقط لكل عملية مباشرة. إنه مصمم لاستيعاب حالة الاستخدام الأكثر شيوعًا (؟) لجلب البيانات عبر الوعود. لكن لكي أكون صادقًا ، أعتقد الآن أنني بحاجة إلى تغييره لمنع الارتباك.

ربما أخطأت لكنني أعتقد أن الشيء الوحيد الذي يمنع تطبيق API المقترح (في هذه المسألة) في userland هو عدم وجود خطافة دورة الحياة التي تنفذ قبل تقديمها مباشرة مثل componentWillUpdate مع props state مثبتة بالفعل على المثيل.

الشيء الوحيد الذي لم تتم مناقشته بعد هو معالجة رد الاتصال onError . إذا ظهر خطأ يمكن ملاحظته ، فيجب أن تكون هذه المعلومات متاحة للمكون بطريقة ما. نظرا لأن الرد يتعامل مع دعوة subscribe(callbacks) ، فإننا نحتاج إلى طريقة موحدة لحقنها في كائن رد الاتصال بهذا.

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

onObserveError(key, error) {
  // do something with the error
  this.state.errors[key] = error;
  this.setState({ errors: this.state.errors });
}

هذا مشابه لما فعلناه في التكرار القادم لـ Parse + React. أنشأنا معالجة الأخطاء الخاصة بنا لإنتاج واجهة برمجة التطبيقات التي أردناها. تتم إضافة الأخطاء إلى خريطة {name => error} الخاصة ، والمكونات لها طريقة عامة ذات مستوى أعلى ، queryErrors() ، تقوم بإرجاع نسخة من الخريطة إذا لم تكن فارغة ، و null خلاف ذلك (السماح if (this.queryErrors()) بسيط

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

andrewimm الفكرة هي إدخال ذلك في نظام انتشار الخطأ العام الذي يقوم بفقاعات خطأ في التسلسل الهرمي حتى يتم التعامل معه بواسطة حد خطأ. https://github.com/facebook/react/issues/2928

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

أود أن أزعم أنه يجب ترك هذا خارج رد الفعل المناسب ، ويجب تنسيق 1 أو 2 من نقاط التكامل الرئيسية مع مشاريع مثل التفاعل غير المتزامن والتفاعل - nexus حتى يمكن القيام بذلك بشكل نظيف فوق React المناسب ...

أوافق ، يبدو أن وجود طريقة مقترحة للقيام بذلك أفضل من
خبز هذا في الإطار نفسه

في الثلاثاء ، 21 أبريل 2015 ، 11:38 مساءً Rodolfo Hansen [email protected]
كتب:

أود أن أزعم أنه يجب ترك هذا خارج رد الفعل المناسب ، ومفتاح 1 أو 2
يجب تنسيق نقاط التكامل مع مشاريع مثل رد فعل غير متزامن و
رد الفعل - nexus لذلك يمكن القيام بذلك بشكل نظيف فوق رد الفعل المناسب ....

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/facebook/react/issues/3398#issuecomment -95048028.

خلال عطلة نهاية الأسبوع ، قمت ببناء تطبيق Flux آخر ، يسمى Flexy . في هذا ، تحقق من رمز المتجر. يعرض طريقة .getObservable التي تتوافق مع واجهة برمجة التطبيقات التي يمكن ملاحظتها ، على الرغم من أنها لا تحتوي بالفعل على أي عناصر قابلة للملاحظة أو أي إطار تفاعلي آخر قيد الاستخدام.

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

ومع ذلك ، لا تحكم على الكود بقسوة ، فقد تم إجراؤه خلال عطلة نهاية الأسبوع من أجل:

  • هزار
  • فهم
  • باستخدام js-csp
  • باستخدام واجهة برمجة التطبيقات لمراقبة

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

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

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

ما الخطافات التي نفتقدها؟

مهلا،

لكي نكون صادقين ، في حين أن الخطافات الجديدة كانت ستجعل التنفيذ أسهل ، يمكننا بالتأكيد تحقيق تحميل جانبي للبيانات بدونها ، كما أوضحنا مع react-async و react-nexus .

إذا كان هناك أي شيء ، فإن عرض دورة حياة مثيلات مكونات React ودعم الحفاظ عليها خارج تسلسل React الهرمي المركب قد يساعد. في react-nexus أستخدم instanciateReactComponent الداخلي وأتصل بـ componentWillMount ، componentWillUnmount ، إلخ ، أنا ، وأنا أعتبر هذا الأسلوب هشًا (ماذا لو instanciateReactComponents يعتمد على الثوابت الداخلية التي تتغير في الإصدار التالي من React؟).

بالنسبة للنهج عديم الحالة ، يبدو لي أن جلب البيانات غير المتزامنة _ is_ الحالة ، وبالتالي تخزين الحالة المعلقة / المكتملة / الفاشلة في حالة بعض المكونات ذات صلة. عندما نستخدم react-nexus في تطبيقات العالم الحقيقي ، فلدينا مكونات ذات ترتيب أعلى تقوم بجلب البيانات وتضخ حالة الجلب الخاصة بها في مكونات الأطفال كدعامات. وبالتالي يكون المكون الداخلي "عديم الحالة" (وهو أمر مرغوب فيه) ، ويكون المكون الخارجي "ذو حالة" (وهو أمر مرغوب فيه أيضًا ، على سبيل المثال ، لعرض قرص تحميل أو عنصر نائب).

ما هي نقاط التكامل الأخرى التي تقترحها؟

يبدو لي أن ANDREYPOPP طرح السؤال الصحيح. أليس الشيء الوحيد الذي نحتاجه لتنفيذ هذا في userland خطاف دورة الحياة قبل التصيير؟ يبدو أنه أصغر تغيير مطلوب لواجهة برمجة التطبيقات ، والباقي هو إعداد وإطلاق ForceUpdate بشكل مناسب أثناء تغيير data بناءً على أي دفق إدخال / باعث / يمكن ملاحظته. ما لم أفقد شيئًا آخر مميزًا بشأنه (ممكن تمامًا)؟

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

  • الخطاف الذي يمكن أن يوفر وظائف يمكنها التعامل مع القيم التي يتم دفعها بواسطة الملاحظة _bقبل_ تعيينها على البيانات. مع المراقبات الحقيقية ، سيكون هذا مجرد .map

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

يبدو لي أن التعليقات القليلة الماضية تقول أن أصغر نقطة امتداد هي في الواقع "توصيل" و "فصل" خطافات دورة الحياة - مما يجعل من السهل إرفاق بيانات غير متزامنة بمكون وإدارة تلك الاشتراكات؟

يمكن بعد ذلك أن تُبنى الملاحظات بشكل تافه إلى حد ما على ذلك (داخل النواة الخارجية أو خارجها) - لكن كشف نقاط نمط الحياة هذه له جاذبية أوسع؟

هل هذا ملخص معقول؟

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

لكن للحظة ، لنفترض أننا نذهب مع observe() . بعض الأفكار:

لدينا this.props ، this.state ، this.context والآن this.data ، كلها مصادر محتملة للبيانات الجديدة في render() . يبدو هذا مبالغا فيه بالنسبة لي. هل فكرة فصل حالة التطبيق عن حالة المكون؟ قد يوضح هذا ويفصل بعض القضايا حول الولاية ، لكني أشعر أن تكلفة إدخال مدخلات جديدة قد لا تفوق المكاسب. إذا أردنا أن يركز this.state فقط على حالة المكون ، فلماذا لا ندع الحقول الموجودة في this.data تضيف إلى this.props أو this.context بدلاً من ذلك؟

الاسم this.data عام جدًا. الدعائم هي بيانات ، والحالة هي بيانات ، وأي متغير محلي هو بيانات. الاسم لا يضيف أي معنى ويشوش المعاني الموجودة. أفضل this.observed أو أي اسم آخر يعني شيئًا ما. لذا +1 تعليق matthewwithanm :

قد يكون هناك اسم أفضل من data ؟ على سبيل المثال ، observed سيربطها بشكل أكثر وضوحًا بالطريقة.

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

إذا استدعينا observe() مرة أخرى في كل مرة props أو state تغيير (و context ؟) ، فيجب تحسين الأداء وأفضل من observe لا يمكن أن تكون باهظة الثمن عن طريق الخطأ. يصبح جزءًا من المسار الساخن. يعجبني هذا من React Nexus:

تكون الارتباطات التالية مختلفة عن الارتباطات السابقة ؛ الارتباطات التي تمت إزالتها غير مُشتركة ، والارتباطات المضافة مُشتركة.

لقد توصلت إلى الاعتقاد بأن الحالة تعقد المكونات وكنت أحاول استخدامها بشكل أقل في المكونات الخاصة بي ورفعها بدلاً من ذلك. لهذا السبب ، بالإضافة إلى المخاوف التي أثارها fisherwebdev (والتي أتفق معها الآن) ، لست مقتنعًا بأن ترك observe يعتمد على state فكرة جيدة. الحالة تعتمد على الدعائم ، الملحوظة تعتمد على الحالة _ و_ الدعائم .. أليست معقدة للغاية؟ أفضل الحصول على observe(props) .

لقد أدركت أيضًا أن الملاحظة يجب ألا تعتمد على state ، غالبًا لأنه لا يحتاج إلى ذلك. كما يشير gaearon ، من السهل بما يكفي رفع الحالة إلى مستوى واحد أعلى ، والذي يشعر بأنه أكثر نظافة بعد فصل المخاوف. عندما يمكن أن يعتمد observe على state ، فإن المنطق حول معالجة التحديثات داخل المكون يصبح أكثر تعقيدًا بشكل ملحوظ ؛ عندما يعتمد ذلك فقط على props ، يمكن أن يكون لديك معالجات بدون شوكة في componentDidMount / componentWillReceiveProps . يُترجم عدد أقل من التعليمات البرمجية في المسار الحرج إلى دورة عرض أبسط ، كما أنه يقلل من عدد التحديثات غير المقصودة التي تعيد تشغيل الاشتراكات.

+1 للمراقبة (الدعائم)

كلما قل تعاملنا مع الحالة ، كانت المنظمة البحرية الدولية أفضل.

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

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

تحرير: آسف ، أدركت للتو أنني كنت أبحث عن flatMap . لقد أربكت نفسي لأنني كنت أفكر في أنها ستؤدي إلى تسطيح مجموعة الرسائل ، لكنها تعمل على مستوى أعلى (الرسائل التي يمكن ملاحظتها).

هل ستتعامل واجهة برمجة التطبيقات المقترحة مع الحالة التي تعتمد فيها نتيجة أحد حقول البيانات على نتيجة حقل آخر؟ أي يمكنك تعيين النتيجة الأولى وإرجاع النتيجة التي يمكن ملاحظتها:

observe(props, context) {
  if (!props.params.threadID) {
    return {};
  }

  const observeThread = ThreadStore.observeGetByID(
    {id: props.params.threadID}
  );
  return {
    thread: observeThread,
    messages: observeThread.map(thread => {
      return MessageStore.observeGetByIDs({ids: thread.messageIDs});
    })
  };
}

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

لا أفهم التعليقات التي لا يجب أن تعتمد على this.state . من المؤكد أن الحالة المغلفة تجعل React أكثر تعقيدًا ولكن هذا أيضًا كل ما هو عليه. إذا لم تكن هناك حالة مغلفة ، فسنحتاج فقط إلى مكتبة الوضع الفوري المذكرة. إذا دخلت في "المتاجر" ، فعندئذٍ ، لا تحتاج إلى ذكر ولكن ليس هذا ما يصفه لك React أنك تفعله.

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

observe() {
  return { items: Items.getPagedItems({ pageIndex: this.state.currentPage }) };
}

حتى لو لم يعتمد observe على state ، فسيظل يعتمد على props و context . حتى لو لم يكن يعتمد على context ، فلا يزال لديك اتجاه يستخدم context لتقديم دعائم المكون التي تستخدمه في observe .

سيحتاج observe إلى إعادة التقييم في كل مرة يخرج فيها تصريح التجسيد لأن الدعائم كان من الممكن أن تتغير. ومع ذلك ، فإننا بالتأكيد نختلف في الملحوظات الناتجة ولن نلغي الاشتراك / إعادة الاشتراك إذا تم إرجاع نفس ما يمكن ملاحظته. ومع ذلك ، لا يمكننا أن نختلف في الخصائص الفردية (بخلاف shouldComponentUpdate) لذا من المثالي أن تقوم بتنفيذ ذاكرة التخزين المؤقت الخاصة بك باستخدام Map كميزة طاقة. بهذه الطريقة يمكنك إرجاع نفس العناصر التي يمكن ملاحظتها إلى عدة مكونات في الشجرة. على سبيل المثال ، مكونات متعددة تحمل نفس المستخدم. حتى إذا لم تقم بذلك ، فأنت تقوم فقط بإعادة إنشاء Observable وتصل في النهاية إلى ذاكرة التخزين المؤقت السفلية. هذه هي الطريقة التي تعمل بها تسوية React على أي حال. إنه ليس بتلك البطء.

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

الذي يقودني إلى النقطة النهائية...

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

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

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

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

أتمنى لو كان شيء مثل هذا موجودًا في المستندات كـ "فلسفة / أهداف تصميم / غير أهداف".

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

احب هذا. أتفق مع gaearon أنه سيكون من الرائع لو كان هذا في نوع ما من المستندات.

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

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

jquense أتفق

كان التردد قبل عامين هو أنه لا يزال بعيدًا جدًا عن التقييس. أردت بروتوكولًا قياسيًا قبل أن نضيفه أيضًا إلى النواة.

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

أعتقد أن ما حدث مع Promises هو أن واجهة برمجة التطبيقات وقصة تصحيح الأخطاء كانت مفقودة بشدة في مناطق معينة لا تعاني منها Observables. إنها قصة أكثر اكتمالا خارجة عن المألوف حيث كان على شركة "وعود" توحيد الحد الأدنى من الحلول غير الكاملة.

الاختلاف الوحيد في الرأي بشأن: المراقبات التي لاحظتها (لم أستطع المقاومة ، آسف) هي إمكانات Zalgo. ما إذا كانت Observables قد تدفع القيمة بشكل متزامن استجابة للاشتراك أم لا. يبدو أن بعض الأشخاص يعارضونها ، لكن استخدام React لـ Observables سيعتمد على هذا بقدر ما أفهمه. يمكنك التعليق على ذلك؟

بشكل عام ، لا أجد أن Zalgo يمثل مشكلة مع Observables لأن المستهلك يتحكم دائمًا وقد يختار دائمًا غير المتزامن بشيء مثل observeOn .

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

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

لا أجد أن Zalgo يمثل مشكلة أيضًا. ومع ذلك ، باستخدام Observables ، يمكنك استخدام برنامج جدولة لضمان عدم التزامن إذا رغبت في ذلك ، أصبح المجدول الافتراضي الآن غير متزامن حتى تتمكن من استخدام ذلك حسب الحاجة.

sebmarkbage أعتقد أنك عالجت معظم مخاوفي وأرى الآن فائدة إضافة هذا إلى إطار العمل. ومع ذلك ، هل يمكنك التعليق على this.data - (1) هل يمكننا / هل يجب علينا طي هذه الحقول في الدعائم / السياق / الحالة أو (2) هل يمكننا إعادة تسميتها؟

قليلًا من الدراجات النارية ولكن المضي في ذلك على أي حال ... إن مشكلة Zalgo ليست في الحقيقة تتعلق بما إذا كانت مهمة من حيث توقعات API ، إنها واحدة من إمكانية التشغيل المتداخل التي يمكن ملاحظتها وسهولة التنفيذ. عدم وجود اتفاق مبكر حول Zalgo الذي وضع عالم مكتبات Promise في موقف مزعج حيث يجب أن تكون دفاعيًا للغاية عند التعامل مع وعود من libs أخرى. (نقطتي أعلاه مكررة أدناه)

... حيث لا يمكن للمكتبة الواعدة (حتى تلك الخاصة بالشكوى) أن تثق ببعضها البعض ، مما يؤدي إلى التفاف غير ضروري وعمل دفاعي للتأكد من حلها بشكل صحيح

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

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

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

"يجب أن نستخدم عقد RxJS الخاص بـ Observable نظرًا لأن هذا أكثر شيوعًا ويسمح بالتنفيذ المتزامن ، ولكن بمجرد استخدام اقتراح jhusain بشكل أكثر شيوعًا ، سننتقل إلى هذا العقد بدلاً من ذلك."

فقط لإضافة القليل من السياق. هناك مبادرة تدفقات تفاعلية (http://www.reactive-streams.org/) لتوفير معيار لمعالجة التدفق غير المتزامن مع الضغط الخلفي غير المعوق. يشمل هذا الجهود التي تهدف إلى بيئات وقت التشغيل (JVM و JavaScript) بالإضافة إلى بروتوكولات الشبكة.

التطبيقات الرائدة الحالية هي fe Akka Streams أو RxJava. لا أعرف ما إذا كانت RxJs تتوافق بالفعل مع نفس الواجهة الآن ، الواجهة الحالية للمشتركis onSubscribe (Subscription s) ، onNext (T t) ، onCompleted () ، onError (Throwable t).

هل يمكنك إلقاء المزيد من الضوء على ما هو اقتراح jhusain ؟

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

هل هناك موقف أو هدف من هذه المبادرة؟

vladap أعتقد أن هذا هو الاقتراح المذكور منjhusain

لقد قرأت من خلال jhusain ولست متأكدًا من الدافع للانتقال إلى هذه المواصفات في المستقبل. هل هناك أي ميزة محددة؟

تتمتع مواصفات التدفقات التفاعلية بدعم أكبر وهي موجودة بالفعل في الإصدار 1.0 . نظرًا لأن RxJava تنفذ بالفعل هذه المواصفات ، أفترض أن RxJs ستتبعها (لكن لم يتم التحقق منها).

تلخص هذه المدونة الواجهات ببعض الأمثلة باستخدام تدفقات Akka.

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

لا يمكنني العثور على قائمة المنفذين على www.reactive-streams.org الآن ولكن آخر مرة تحققت فيها كانت:

بيورن أنتونسون - شركة Typesafe
جافين بيرمان - Oracle Inc.
جون بريسبان - شركة Pivotal Software Inc.
جورج كامبل - Netflix، Inc
بن كريستنسن - Netflix ، Inc
ماتياس دونيتز - spray.io
ماريوس إريكسن - Twitter Inc.
تيم فوكس - شركة ريد هات.
فيكتور كلانج - شركة Typesafe
رولاند كوهن - شركة Typesafe Inc.
دوج ليا - جامعة ولاية نيويورك أوسويغو
ستيفان مالديني - شركة Pivotal Software Inc.
نورمان مورير - Red Hat Inc.
إريك ميجر - Applied Duality Inc.
تود مونتغمري - Kaazing Corp.
باتريك نوردوال - شركة Typesafe
يوهانس رودولف - spray.io
إندري فارجا - شركة Typesafe

ربما أذهب بعيدًا هنا ولكني أعتقد أن السياق الأكبر يمكن أن يساعد في القرارات المستقبلية.

vladap مما أفهمه وما أراه في مشكلات github يعملjhusain معهم بالفعل ، لذلك أعتقد أننا لن نواجه الكثير من المشاكل.
من منظور الواجهة ، مما يمكنني أيضًا فهمه في مشكلات جيثب المختلفة ومستندات المواصفات الأخرى ، سيحترم المراقب بالتأكيد واجهة المولد ، لذلك شيء مثل:

{
  next(value),
  throw(e),
  return(v)
}

ما عليك سوى تنفيذ طريقة أساسية يمكن ملاحظتها باستخدام طريقة "اشتراك" واحدة تحترم تلك الواجهة يجب أن تكون آمنة للتفاعل.

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

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

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

أين يمكنني العثور على واجهات يمكن ملاحظتها مقترحة لـ ECMAScript في المستقبل؟ إذا كان هناك أي بالفعل.

يمكن العثور على الاقتراح الحالي هنا:

https://github.com/jhusain/asyncgenerator

JH

في 7 مايو 2015 ، الساعة 2:32 صباحًا ، كتب vladap [email protected] :

أين يمكنني العثور على واجهات يمكن ملاحظتها مقترحة لـ ECMAScript في المستقبل؟ إذا كان هناك أي بالفعل.

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub.

يذهب مقترح التدفقات التفاعلية (RSP) إلى أبعد من مقترح TC-39 لأنه يقدم خاصية الملاحظة التي تتعامل مع الضغط الخلفي. تم تحسين RSP Observable لإرسال التدفقات بكفاءة عبر الشبكة ، مع احترام الضغط الخلفي. يعتمد جزئيًا على العمل المنجز في RxJava ، وهو عمل هندسي مثير للإعجاب (الكشف الكامل: تم تصميمه بواسطة زميل في Netflix ، Ben Christensen).

السبب الرئيسي لقرار توحيد النوع القابل للرصد الأكثر بدائية هو الحذر. الأكثر بدائية يمكن ملاحظته هو ثنائي ES2015 Iterable Contract ، والذي يوفر لنا ضمانات قيمة بأن النوع مرن على الأقل مثل النوع القياسي بالفعل في ES2015. علاوة على ذلك ، هناك مجموعة متنوعة من حالات الاستخدام في JS for Observables والتي لا تتطلب ضغطًا عكسيًا. في المتصفح ، يعد DOM هو المصدر الأكثر شيوعًا لتدفقات الدفع ، ويعمل بشكل فعال مثل المخزن المؤقت. نظرًا لأن نوع RSP أكثر تعقيدًا ، فإن نهجنا هو توحيد النوع الأكثر بدائية أولاً ، ثم ترك مساحة لتنفيذ النوع الأكثر تقدمًا لاحقًا. من الناحية المثالية ، ننتظر حتى يتم التحقق من صحتها في أرض المستخدم.

ليس لدى FYI RxJS حاليًا أي خطط لتنفيذ RSP المرصود.

JH

في 7 مايو 2015 ، الساعة 2:30 صباحًا ، كتب vladap [email protected] :

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

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

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

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub.

شكرا جزيلا على التفاصيل القيمة. إنه منطقي جدا.

gaearon أنا قمت بنسخك. أردت استخدام Parsereact مع فئات ES6، لذلك احتاجت إلى إعادة تشغيل واجهة برمجة تطبيقات Mixin المراقبة كمكون من مكون أعلى للطلب.

https://gist.github.com/amccloud/d60AA92797B932F72649 (الاستخدام أدناه)

  • أسمح بالاحتفال بالتحديد في المكون أو مرت إلى Decorator. (يمكنني دمج الاثنين)
  • أقوم بالدمج في ملحقات كقوائم (لا this.data، أو this.props.data)

aaronshafgaearon فائدة جعلها من الدرجة الأولى هي:

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

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

بدلاً من المكوّن ذو الترتيب الأعلى ، يمكن أن يكون مكونًا عاديًا:

import Observe from 'react/addons/Observe';

class Foo {
  render() {
    return (
      <Observe
        render={this.renderData}
        resources={{
          myContent: xhr(this.props.url)
        }} />
    );
  }

  renderData({ myContent }) {
    if (myContent === null) return <div>Loading...</div>;
    return <div>{myContent}</div>;
  }
}

نظرًا لأنك تتحكم في الوظيفة التي تم تمريرها كـ render prop ، فلا توجد طريقة لتضارب الأسماء. من ناحية أخرى ، لا تلوث حالة المالك.

ما الذي افتقده هنا؟

قد يكون هذا أقل تفصيلاً إذا كان المكون Observe قد حصل للتو على Observables من دعائمه:

render() {
  return (
    <Observe myContent={Observable.fetch(this.props.url)}
             render={this.renderData} />
  );
}

renderData({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

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

أخيرًا ، يمكن للمرء أن يكتب مصممًا مقابل render يلفه في مكون Observe :

@observe(function (props, state, context) {
  myContent: Observable.fetch(props.url)
})
render({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

أفضل الاحتفاظ بـ render كدالة عرض نقية بدلاً من إدخال منطق جلب البيانات فيه.
الاقتراح الأولي يبدو جيدًا في رأيي. إنها قريبة جدًا من كيفية عمل الحالة مع rx-react وستسمح بفصل إدارة الحالة عن منطق جلب البيانات الذي يبدو متماسكًا للغاية.

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

إنه لا يقوم بحقن منطق جلب البيانات ، فقط يحفظ بعض الكتابة. سيتم إلغاء الأمر إلى الإصدار أعلاه ، والذي يعرض المكون <Observe /> . ليس من غير المعتاد تقديم مكونات ذات حالة جيدة ، لذلك لا أعتقد أنها تجعل render أقل نقاء مما هو عليه الآن.

حاولت الجمع بين الأفضل من # 3858 وهذا الاقتراح.

في أي نهج ذي ترتيب أعلى ، تكون الفائدة صريحة ، لكن الجوانب السلبية موصوفة بواسطةsebmarkbage : قد تتعارض أسماء الدعائم في مرحلة ما.

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

في # 3858 ، الفائدة هي تحديد موقع تبعيات "العرض المذكر" مع العرض نفسه (لا يتم استخدامها في أي مكان آخر ، لذا من المنطقي) ، ولكني قلق بشأن "يبدو متزامنًا ولكنه غير متزامن" ، ولا أفهم كيف يمكن أن تعمل مع نماذج غير قابلة للتغيير إذا كانت تعتمد على this كثيرًا . كما أنه يجعلني مخطئًا في طريقة React-was-easy-to-reason-about لأنه لا يوجد شيء سهل حول التفكير في تتبع التغييرات يدويًا وربط مصادر البيانات بـ React (أو تغليفها للعمل مع React). أنا أفهم أن هناك ضغطًا لتنفيذ شيء مؤدٍ ويقلل من المستوى المعياري على الرغم من ذلك.

في اقتراحي ، سأحتفظ بالموقع والصراحة ، لكن:

  • <Observe /> (أو المصمم observe() الذي يلتف مع <Observe /> ) هو مجرد إضافة ، أنا لا أقترح _ أي _ تغييرات على React core.
  • يمكن للجميع تنفيذ منطق المراقبة الخاص بهم في حالات الاستخدام الخاصة بهم. يمكنك الحصول على <Observe /> الخاص بك الذي يستخدم واحدًا فقط يمكن ملاحظته ، إذا كان هذا هو ما تفضله. يجوز لك أو لا يجوز استخدام المصمم.
  • تظل دورة حياة المكون كما هي.
  • لا توجد تعارضات في الخاصية لأنه يتم تمرير البيانات كمعامل.
  • نحن نقوم بالتجربة من خلال حل المشكلات باستخدام الأدوات التي لدينا.
  • لتقليل النموذج المعياري ، نستخدم أدوات تقليل النماذج المعيارية (أدوات تزيين) بدلاً من تقديم مفاهيم أساسية جديدة.

مناقشة رائعة والعمل في كل مكان هنا ، الكثير من الاحترام. :)

أوافق على أن هذا لا يستحق تحويله إلى جوهر. ربما تعطي الوظيفة الإضافية هذا الاقتراح قوة جذب كافية يمكن للناس محاولة التقارب فيه وتوحيده قبل الالتزام بشكل كامل. ومع ذلك ، أجد هذا الاقتراح أفضل من https://github.com/facebook/react/issues/3858 و https://github.com/facebook/react/pull/3920 لبساطته.

هذا شيء كنت أستخدمه في المشاريع الجانبية (حبوب الملح) - إنه مشابه لعمل elierotenberg الرائع ولكنه لا

جهز نفسك ل CoffeeScript و mixins ، أو استمر في التحديق حتى يبدو هذا مثل ES6 إذا كنت تفضل ذلك. :)

_ = require 'lodash'

module.exports = DeclareNeedsMixin = 
  componentDidMount: ->
    <strong i="12">@needsConsumerId</strong> = _.uniqueId @constructor.displayName
    <strong i="13">@sinkNeeds</strong> <strong i="14">@props</strong>, <strong i="15">@state</strong>

  componentWillUpdate: (nextProps, nextState) ->
    <strong i="16">@sinkNeeds</strong> nextProps, nextState

  componentWillUnmount: ->
    @props.flux.declareNeeds <strong i="17">@needsConsumerId</strong>, []

  sinkNeeds: (props, state) ->
    if not @declareNeeds?
      return console.warn 'Missing method required for DeclareNeedsMixin: `declareNeeds`', @

    needs = <strong i="18">@declareNeeds</strong> props, state
    props.flux.declareNeeds <strong i="19">@needsConsumerId</strong>, needs

  # Intended to be overridden by the host class.
  # Returns a set of facts, stored as an array.
  # Yes, immutable data is awesome, that's not the point here though. :)
  # Facts are serializable data, just values.
  # declareNeeds: (props, state) ->
  #   []

وتستخدم مثل هذا:

module.exports = EmailThreads = React.createClass
  displayName: 'EmailThreads'
  mixins: [DeclareNeedsMixin]

  propTypes:
    flux: PropTypes.flux.isRequired

  declareNeeds: (props, state) ->
    [Needs.GmailData.myThreads({ messages: 20 })]

  ...

لذا فإن declareNeeds هي دالة ذات اتجاه واحد للدعامات وتذكر وصفًا لما يحتاجه هذا المكون. في التنفيذ الفعلي هنا ، فإن الطرف المستلم @props.flux.declareNeeds ، والذي تم إعداده في مكون المستوى الأعلى ، يغرق هذه الاحتياجات في كائن ProcessSink . يقوم بدفعات كما يحلو لها ، ويفكك needs عبر المكونات التي تشترك في نفس flux ، ثم يقوم بآثار جانبية لتلبية تلك الاحتياجات (مثل الاتصال بمقبس أو إجراء طلبات HTTP). يستخدم العد المرجعي لتنظيف الأشياء ذات الحالة مثل اتصالات المقابس عندما لا تكون هناك مكونات تحتاج إليها بعد الآن.

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

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

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

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

يقدمelierotenberg و andrewimm نقطة ممتازة حول معالجة الأخطاء من الدرجة الأولى. sebmarkbage أعتقد أن غريزتك تجاه الحد الأدنى من نقطة التشغيل المتداخل تعمل بشكل صحيح ، لكنني لست متأكدًا من كيفية إضافة دلالات للأخطاء لتكوين فقاعة لشجرة المكون يتناسب مع هذا المطلب. يجب أن تكون هناك قصة بسيطة بنفس القدر هنا حول كيفية الوصول إلى القيم من onError و onCompleted ، حتى لو كانت تلك الفتحات الموجودة في this.observed تحمل القيمة الأخيرة لأي من next/error/completed مثل { next: "foo" } . بدون دعم العقد الذي يمكن ملاحظته كميزة من الدرجة الأولى ، فأنا متشكك بعض الشيء بشأن هذا الاقتراح الذي يؤدي إلى الخفض.

ونظرًا لأن هذا هو الإنترنت ، وكانت إحدى المرات الأولى التي أتحدث فيها هنا: خلاصة مشكلات React هي بعض من أفضل القراءة ومصدر شامل للعمل والأفكار الرائعة. : +1:

تنبعث منه رائحة مثل الارتباطات بالنسبة لي.

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

خذ المثال التالي مع react-nexus@^3.4.0 :

// the result from this query...
@component({
  users: ['remote://users', {}]
})
// is injected here...
@component(({ users }) =>
  users.mapEntries(([userId, user]) =>
    [`user:${userId}`, [`remote://users/${userId}/profile`, {}]]
  ).toObject()
))
class Users extends React.Component {
  // ... this component will receive all the users,
  // and their updates.
}

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

ومع ذلك ، كما لاحظ sebmarkbage ، هناك خطر تلويث مساحة اسم الدعائم بدلاً من ذلك. في الوقت الحالي ، أستخدم أداة تزيين محولات الدعائم ( react-transform-props ) لتنظيف / إعادة تسمية الدعائم قبل تمريرها إلى المكون الداخلي ، لكنني أقر أنها قد تصبح مشكلة في المستقبل إذا أصبحت المكونات ذات الترتيب الأعلى أكثر شيوعًا وتزداد مخاطر تضارب الأسماء.
هل يمكن حل ذلك باستخدام الرموز هي مفاتيح الخصائص؟ هل يدعم فحص propTypes Symbol الدعائم ذات المفاتيح؟ هل ستدعم JSX مفاتيح الخصائص المضمنة المحسوبة (على الرغم من أنه يمكن للمرء دائمًا استخدام الخصائص المحسوبة + انتشار الكائن)؟

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

بلدي 2 ¢

لقد استمتعت كثيرًا بنمط "الأطفال كوظيفة" للقيم التي تتغير بمرور الوقت. أعتقد أن elierotenberg جاء به لأول مرة؟ استخدامي الحالي هو نمذجة الينابيع (عبر الارتداد) على نوابض التفاعل. مثال -

<Springs to={{x: 20, y: 30}} tension={30}>
  {val => <div style={{left: val.x, top: val.y}}>moving pictures</div>}
</Springs>

لا تصادم الدعائم ولا استخدام لحالة المالك. يمكنني أيضًا تداخل عدة نوابض ، والتفاعل مع كل الأجزاء الصلبة. يمكن أن تقبل هذه "العناصر القابلة للملاحظة" (ha!) أيضًا onError و onComplete وعناصر أخرى (استعلامات الرسم البياني؟).

محاولة تقريبية لرسم هذا من أجل "التمويه"

<Store 
  initial={0}
  reduce={(state, action) => action.type === 'click'? state+1 : state} 
  action={{/* assume this comes as a prop from a 'Dispatcher' up somewhere */}}> 
    {state => <div onClick={() => dispatch({type: 'click'})}> clicked {state} times</div>}
</Store>

استخدمنا هذا النمط ، الذي أطلقنا عليه _render callbacks_ ، في Asana ولكننا في النهاية نتحرك بعيدًا عنه.

الايجابيات

  • لا يوجد احتمال لتصادم الدعائم.
  • من السهل تنفيذ كل من مؤلف StoreComponent ومستخدم StoreComponent

سلبيات

  • من الصعب للغاية تنفيذ shouldComponentUpdate نظرًا لأن استدعاء رد الاتصال قد يغلق على الحالة. تخيل أن لدينا شجرة مكونة A -> Store -> B . A على عداد في حالته التي يتم الوصول إليها أثناء رد استدعاء العرض كدعم لـ B . إذا A تحديثات بسبب العداد و Store لا التحديث، B سوف يكون لديك نسخة قديمة من العداد. نتيجة لذلك ، اضطررنا دائمًا إلى تحديث المتجر.
  • أصبح اختبار المكونات في عزلة أمرًا صعبًا للغاية. حتمًا ، يضع المطورون منطقًا معقدًا في عمليات رد نداء العرض الخاصة بالمكون الذي سيتم عرضه ويريدون اختبار المنطق. كانت الطريقة الطبيعية للقيام بذلك هي عرض الشجرة بأكملها واختبار _ من خلال_ مكون المخزن. هذا جعل من المستحيل استخدام العارض الضحل.

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

نقاط رائعة ، لا يمكن التفكير في طريقة للتغلب على أي منهما دون كسر شرط "جانبية".

threepointone يبدو تمامًا مثل أحد مقترحات التنفيذ https://github.com/reactjs/react-future/pull/28

@ pspeter3 في shouldComponentUpdate: ()=> true ؟ أعتقد أنه كان من الممكن عرض "الأطفال" بدون Store في السلسلة على أي حال. شكرا على وقتك!

threepointone هذا بالضبط ما فعلناه لحدودنا. لم يكن من الواضح ما هو التأثير بالضبط ولكن كانت هناك مخاوف بشأن الأداء من أعضاء الفريق. أدت المخاوف المقترنة بصعوبة الاختبار إلى إجبار التحول إلى استخدام React.cloneElement(this.props.child, {data: this.state.data})

@ pspeter3 زاوية الاختبار هي بالتأكيد مشكلة. ماذا لو تعرف العارض السطحي على "عرض عمليات الاسترجاعات"؟ هل سيساعد ذلك؟

Ps- "رد الاتصال": thumbs_up:

sebmarkbage المناقشة الجارية على أكثر من ES-ملاحظتها هي أن subscribe سيتم مقارعة المتزامن، مع [Symbol.observer] طريقة المقدمة للاختصار والاشتراك بشكل متزامن، على الرغم من صحة وجود كل متزامنة / المتزامن واجهات برمجة التطبيقات غير قيد المناقشة حاليا.

لقد دخلت في هذه التذكرة مع حالة الاستخدام المذكورة أعلاه لصالح الاشتراك المتزامن ، ولم أكن أعرف ما إذا كان لديك أي شيء تضيفه هناك.

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

أيضًا gaearon قمت بتبسيط بت ComposedComponent.observe وباستخدام this.state بدلاً من this.state.data - https://gist.github.com/tgriesser/d5d80ade6f895c28e659

يبدو رائعًا حقًا مع مصممي الديكور es7 المقترحين:

<strong i="20">@observing</strong>
class Foo extends Component {
  static observe(props, context) {
    return {
      myContent: xhr(props.url)
    };
  }
  render() {
    var myContent = this.props.data.myContent;
    return <div>{myContent}</div>;
  }
}

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

قد يكون عدم وجود اشتراك متزامن مشكلة كبيرة.

أعتقد أنني فهمت كيفية حل "مشكلة الحالة" و "تحميل البيانات الجانبية" (البيانات المشتقة) بطريقة متسقة. يجعله في "طريقة رد الفعل" عديمة الحالة. لقد وجدت طريقة للحفاظ على تناسق الحالة في أي وقت وتتناسب مع النمط UI = React(state) . من غير المحتمل أن أكون خارج السيطرة لجعله مقاومًا للرصاص تمامًا ، وأضف المزيد من الأمثلة وقدم عرضًا جيدًا. https://github.com/AlexeyFrolov/slt . من ناحية أخرى ، تم اختباره جيدًا وأنا أستخدمه في مشاريع الإنتاج الخاصة بي بشكل متكرر. العقول الذكية هي موضع ترحيب للمساهمة.

أهلا بكم،

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

نظرًا لعدم تمكننا من العثور على أي شيء متاح جاهز للاستخدام ، قمنا ببناء lib الخاص بنا ، بناءً على مبادئ الضربة القاضية للملاحظة (خاصة: الاشتراكات التلقائية).
يتناسب هذا تمامًا مع دورة الحياة الحالية لمكونات React ، ولم نكن بحاجة إلى عمليات اختراق غريبة أو حتى عمليات إعادة استدعاء لعرض الطفل (تبلغ مساحة ObserverMixin أدناه حوالي 10 مواضع).
أدى هذا إلى تحسين DX لدينا كثيرًا وعمل بشكل جيد للغاية لفريقنا ، لذلك قررنا نشره كمصدر مفتوح . في هذه الأثناء ، تم إثبات نجاحها في المعركة (على سبيل المثال ، توفر مصفوفة يمكن ملاحظتها ES7) ومُحسّنة للغاية.
فيما يلي مثال مؤقت قصير ، (متاح أيضًا باسم JSFiddle ) ، لا يمكن أن يكون IMHO و DX أفضل بكثير ...: مرتاح:

var store = {};
// add observable properties to the store
mobservable.props(store, {
    timer: 0
});

// of course, this could be put flux-style in dispatchable actions, but this is just to demo Model -> View
function resetTimer() {
    store.timer = 0;
}

setInterval(function() {
    store.timer += 1;
}, 1000);

var TimerView = React.createClass({
    // This component is actually an observer of all store properties that are accessed during the last rendering
    // so there is no need to declare any data use, nor is there (seemingly) any state in this component
    // the combination of mobservable.props and ObserverMixin does all the magic for us.
    // UI updates are nowhere forced, but all views (un)subscribe to their data automatically
    mixins: [mobservable.ObserverMixin],

    render: function() {
        return (<span>Seconds passed: {this.props.store.timer}</span>);
    }
});

var TimerApp = React.createClass({
    render: function() {
        var now = new Date(); // just to demonstrate that TimerView updates independently of TimerApp
        return (<div>
            <div>Started rendering at: {now.toString()}</div>
            <TimerView {...this.props} />
            <br/><button onClick={resetTimer}>Reset timer</button>
        </div>);
    }
});

// pass in the store to the component tree (you could also access it directly through global vars, whatever suits your style)
React.render(<TimerApp store={store} />, document.body);

لمزيد من التفاصيل حول هذا النهج ، راجع هذه المدونة . راجع للشغل ، سأتأكد من إضافة المصمم و / أو الحاوية إلى lib ، لأولئك الذين يستخدمون فئات ES6.

للأسف ، لم أر هذا الخيط قبل رد الفعل الأوروبي ، وإلا لكان لدينا فرصة جيدة لمناقشة React والمالحظات. لكن شكراً جزيلاً على المحادثات الملهمة! : +1: أحببت بشكل خاص تجريدات GraphQL وأعمال الفكر وراء Redux :)

mweststrate أشعر أن المجتمع سينتهي به المطاف بالحاجة إلى الاختيار بين حلول "المراقبة" و "البيانات غير القابلة للتغيير" في النهاية. ربما نحتاج إلى المزج بطريقة ما للحصول على نقاط القوة لكلا النهجين في الحل الواحد (https://github.com/AlexeyFrolov/slt/issues/4). في الحل الذي قدمته ، قمت بتطبيق نهج "البيانات غير القابلة للتغيير" مع التركيز على اتساق الحالة في أي وقت. أخطط لدعم المراقبين والمولدات أيضًا. هذا مثال على القواعد التي تستخدم لجلب البيانات "المشتقة" أو "التكميلية" (مثل نص الصفحة ، والأصول ، والتوصيات ، والتعليقات ، والإعجابات) والحفاظ على اتساق حالة التطبيق.

https://github.com/AlexeyFrolov/slt#rules -example

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

import r from "superagent-bluebird-promise";
import router from "./router";

export default {
    "request": function (req)  {
        let route = router.match(req.url);
        let session = req.session;
        route.url = req.url;
        return this
            .set("route", route)
            .set("session", req.session);
    },
    "route": {
        deps: ["request"],
        set: function (route, request) {
            let {name, params: { id }} = route;
            if (name === "login") {
                return this;
            }
            let url = router.url({name, params: {id}});
            let method = request.method ? request.method.toLowerCase() : "get";
            let req = r[method]("http://example.com/api/" + url);
            if (~["post", "put"].indexOf(method)) {
                req.send(request.body);
            }
            return req.then((resp) => {
                let ctx = this.ctx;
                let path = url.substr(1).replace("/", ".");
                if (!resp.body) {
                    let location = resp.headers.location;
                    if (location) {
                        ctx.set("request", {
                            method: "GET",
                            url: location.replace('/api', '')
                        });
                    }
                } else {
                    ctx.set(path, resp.body);
                }
                return ctx.commit();
            });
        }
    }
}

في البقية ، نفس النهج الذي تتبعه ، باستثناء عدم وجود ارتباط مباشر بـ React (ليس مطلوبًا في حالتي). أعتقد أننا بحاجة إلى توحيد الجهود بطريقة ما لأرشفة الهدف المشترك.

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

من التعليقات أعلاه ، يمكنني سرد ​​النطاقات الممكنة التي يحتاج المستخدم النهائي إلى التفكير فيها في كل مرة يريد فيها إنشاء مكون "مليء بالبيانات":

  • التقديم من جانب الخادم
  • بدء تشغيل .state و .data
  • .context ، .props ، .state و .observe تشابك
  • العرض غير المتزامن

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

أنا أتفق مع خطافات دورة الحياة glenjamin connect و disconnect .

أعتذر إذا كان هذا خارج الموضوع (لا أستطيع أن أخبرك تمامًا). ولكن إليك مثال على سبب اعتقادي أن كشف واجهة برمجة التطبيقات للتفاعل مع شجرة المكونات سيكون طريقة مثيرة للاهتمام للتعامل مع هذه المشكلة: https://github.com/kevinrobinson/redux/blob/feature/loggit-todomvc/examples/loggit -todomvc / loggit / renderers / precompute_react_renderer.js # L72

هذه الطريقة هي القرصنة الخاصة بي لأتمكن من السير في الشجرة ، وبالتالي فإن سؤالي هو كيف يمكن لوجود واجهة برمجة تطبيقات عامة للقيام بهذا النوع من الأشياء تمكين الابتكار هنا في "تحميل البيانات بشكل جانبي" ، والذي أعتقد أنه يتلخص في "عارض لا يفعل ذلك" t العمل من أعلى إلى أسفل ": https://github.com/kevinrobinson/redux/blob/feature/loggit-todomvc/examples/loggit-todomvc/loggit/react_interpreter.js#L8

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

أعتقد أن swannodette تحدثت عن هذا في ReactConf. أتساءل عما إذا كان Om Next يفعل ذلك؟

نعم ، لقد اقترح نفس الشيء في أول ReactConf. لم أشاهد أحدث إصدار من Om.next أو أسمع حديث EuroClojure ، ولكن في السابق كانت Om تستخدم هيكلها الخاص الذي كان على المستخدمين البناء عليه من أجل القيام بذلك.

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

أعتقد أن هذا يكافئ تقريبًا العرض الضحل الموجود في أدوات الاختبار - لقد ذكرت إنشاء واجهة برمجة تطبيقات من الدرجة الأولى كأحد تطبيقات DOM & String إلى sebmarkbage في ReactEurope - يبدو أن التغييرات 0.14 تمهد الطريق جيدًا لذلك.

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

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

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

نظرًا لتعقيد ما يمكن ملاحظته ، أقترح بتواضع السادة المحترمين في هذا الموضوع للنظر في تنفيذ Meteor للبيانات التفاعلية: https://github.com/meteor/meteor/wiki/Tracker-Manual. تتطلب عملية التوفيق بينها وبين React حرفياً 3-5 أسطر من التعليمات البرمجية في طريقة componentWillMount ، شيء مثل:

  componentWillMount() {
    if (typeof this.getState === 'function') {
      Tracker.autorun(() => {
        // Assuming this.getState() calls some functions that return
        // reactive data sources
        this.setState(this.getState());
      });
    }
  }

لا أعرف ما إذا كان Tracker (الذي يتم استخراجه بسهولة كمكتبة منفصلة) يغني عن الحاجة إلى دعم يمكن ملاحظته في React ، ولكن من المؤكد أنه يبدو بهذه الطريقة.

تتبع MOBservable نمطًا مشابهًا جدًا لتحديث طرق جانبية للمكون بمجرد تغيير بعض القيم التي يمكن ملاحظتها ، لذلك يبدو أن طرق دورة الحياة الحالية + أدوات التزيين توفر مرونة كافية لمكتبات الطرف الثالث للتعبير عن هذا النوع من الأنماط و imho مفهوم مصدر بيانات ثالث لن يؤدي إلا إلى تعقيد الأمور.

    componentWillMount: function() {
        var baseRender = this.render;
        this.render = function() {
            if (this._watchDisposer)
                this._watchDisposer();
            var[rendering, disposer] = mobservableStatic.watch(() => baseRender.call(this), () => {
                    this.forceUpdate();
            });
            this._watchDisposer = disposer;
            return rendering;
        }
    },

Mitranim أوافق ، هذه قراءة جيدة حقًا ، شكرًا للعثور على ذلك! إنه بشكل فعال ما يقترحه https://github.com/facebook/react/pull/3920 .

نحن مترددون بشأن أي اقتراح أفضل. بعد أن لعبت مع كليهما ، فأنا مقتنع في الغالب بأن البرمجة التفاعلية (كما ربطت ؛ https://github.com/meteor/meteor/wiki/Tracker-Manual) هي السبيل للذهاب ، لكننا لم نتوصل إلى إجماع و ما زلنا نحاول معرفة ما هو أكثر منطقية ، لذلك نرحب بالتعليقات.

Mitranimjimfb لقد كنت من أشد المعجبين بـ Meteor منذ عدة سنوات حتى الآن. المقتفي رائع حقًا ورائع جدًا. لقد قمت بإنشاء عرض تقديمي يوضح كيفية عمل إصدار بسيط جدًا من أداة التتبع:

https://github.com/ccorcos/meteor-track/blob/master/client/main.js

ولقد قمت حتى بإنشاء مكتبة تدفقات يمكن ملاحظتها باستخدام Tracker:

https://github.com/ccorcos/meteor-tracker-streams

ولكن نظرًا لأنني انتقلت تمامًا إلى استخدام React بدلاً من Blaze ، أدركت أن Tracker معقد للغاية وأحيانًا تكون طرق النشر / الاشتراك / onChange البسيطة أسهل 100 مرة.

أيضًا ، بالنسبة لجميع محبي البرمجة الوظيفية ، يأتي Tracker مع بعض الآثار الجانبية التي تكون منطقية بنسبة 99٪ من الوقت ، ولكن في بعض الأحيان يحصلون عليها. وإليك كيف ستبدو في مكون React

componentWillMount: ->
  <strong i="15">@c</strong> = Tracker.autorun =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})
componentWillUnmount: ->
  @c.stop()

وجهة نظري حول الآثار الجانبية هي أن c.stop() سيوقف الاشتراك والتشغيل التلقائي الداخلي أيضًا.

ccorcos نعم ، في https://github.com/facebook/react/pull/3920 ستكون جميع الآثار الجانبية داخلية تمامًا لـ React. في الواقع ، يمكن لنواة React أن تنفذها بطريقة وظيفية / ثابتة تمامًا لا تؤدي إلى أي طفرات. لكن هذه تفاصيل تنفيذية ، نظرًا لأن الآثار الجانبية لن تكون مرئية من الخارج على أي حال.

مثير للاهتمام ... فلماذا لا تستخدم فقط عمليات رد النداء onChange؟ لقد وجدت أن هؤلاء هم الأكثر توافقًا. سواء كنت تستخدم Meteor (Tracker) ، أو RxJS ، أو Highland.js ، وما إلى ذلك ، يمكنك دائمًا الاندماج معها بشكل تافه مع رد اتصال حدث. ونفس الشيء مع مكون React عالي الترتيب.

ما أحبه في Redux هو أنه يبقي هذا المنطق خارج إطار العمل ويحافظ على مكونات React كوظائف خالصة.

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

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

نعم ، ولكن ليس حقًا إلغاء الاشتراك "تلقائيًا". يجب عليك استدعاء c.stop() في المكون سيتم إلغاء تحميله. يمكنك تجريد ذلك بعيدًا باستخدام mixin.

componentWillMount: ->
  <strong i="7">@autorun</strong> =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})

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

ccorcos قد نتحدث عن آليات مختلفة. آخر مرة راجعت ، لم يكن لدى Tracker اشتراكات دائمة ؛ يتم إعادة تشغيل كل دالة تنشئ اعتمادًا على مصدر بيانات تفاعلي (من خلال الوصول إليه) عندما تتغير ، وهذا هو نهاية الاشتراك. إذا قام بالوصول إلى مصدر البيانات مرة أخرى ، فإن هذا يعيد تأسيس "الاشتراك" لتغيير واحد آخر. وما إلى ذلك وهلم جرا.

Mitranim صحيح ، دلالات # 3920 هي أكثر "تلقائية" من Meteor (إلغاء الاشتراك تلقائي بالفعل) ، وأبسط بكثير نظرًا لوجود مساحة سطح API صفرية حرفيًا في حالة الاستخدام الشائع.

ccorcosMitranim للحصول على مكتبة مستوحاة من Tracker / Vue.js جاهزة للاستخدام ، يمكنك تجربة Mobservable ، فهي تراقب جميع البيانات التي يتم الوصول إليها أثناء _render_ ، وتتخلص من جميع الاشتراكات عند إلغاء التحميل (حتى ذلك الحين تظل الاشتراكات على قيد الحياة). طبقناها بنجاح على مشاريع كبيرة جدًا حتى الآن في Mendix. إنه غير مزعج إلى حد كبير لبياناتك كما أنه يزين الكائنات الموجودة بدلاً من توفير كائنات النموذج الخاصة به.

آخر مرة راجعت ، لم يكن لدى Tracker اشتراكات دائمة

يمكن أن تكون اشتراكات Mitranim دائمة. تحقق من هذا.

sub = Meteor.subscribe('chatrooms')
# this subscription lasts until...
sub.stop()

الآن هناك بعض الأشياء المثيرة للاهتمام مع Tracker. تحقق من هذا.

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')
# this subscription lasts until...
comp.stop()

لكن المثال الأخير ليس مفيدًا بشكل رهيب. حتى نقوم بشيء مثل هذا.

roomId = new ReactiveVar(1)
comp = Tracker.autorun ->
  Meteor.subscribe('messages', roomId.get())
# when I change the roomId, the autorun will re-run
roomId.set(2)
# the subscription to room 1 was stopped and now the subscription to room 2 has started
# the subscription is stopped when I call stop...
comp.stop()

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

يستمر الاشتراك حتى يتم إعادة التشغيل التلقائي (تم تغيير التبعية التفاعلية) أو الحساب الذي يتوقف عليه. كلاهما يستدعي الحساب. onInvalidate hook.

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

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')

# is the same as

comp = Tracker.autorun (c) ->
  sub = null
  Tracker.nonreactive ->
    # dont let comp.stop() stop the subscription using Tracker.nonreactive
    sub = Meteor.subscribe('chatrooms')
  c.onInvalidate ->
    # stop the subscription when the computation is invalidated (re-run)
    sub.stop()
# invalidate and stop the computation
comp.stop()

ccorcos أرى مصدر الارتباك الآن ؛ كانت مفرداتي. عند الحديث عن الاشتراكات ، لم أقصد _ اشتراكات النيزك_. إنهم يحددون البيانات التي يتم دفعها من الخادم إلى العميل ، ولكنها ليست ذات صلة بتحديثات طبقة العرض ، والتي هي موضوع هذه المناقشة. عندما أقول _subscription_ ، كنت أرسم توازيًا بين مستمعي الأحداث التقليديين وقدرة Tracker على إعادة تشغيل وظيفة تعتمد على مصدر بيانات تفاعلي. في حالة مكونات React ، ستكون هذه طريقة مكونة تجلب البيانات ثم تستدعي setState أو forceUpdate .

mweststrate شكرًا على المثال ، يبدو ممتعًا.

أه نعم. لذلك لديهم طريقة ذكية للقيام بذلك.

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    let sub = Meteor.subscribe('messages')
    return {
      loading: !sub.ready(),
      messages: Messages.find().fetch()
    }
  })
componentWillUnmount: function() {
  this.comp.stop()
}

سيتم إعادة الاشتراك في كل مرة دون مشاكل. يعتبر كل من sub.ready و Messages.find.fetch "تفاعلين" وسيؤديان إلى إعادة تشغيل التشغيل التلقائي كلما تغيروا. ما هو رائع في Tracker هو عندما تبدأ في إخفاء عمليات التشغيل التلقائية وتوجد فقط في الوثائق التي تشير إلى وجود وظيفة معينة ضمن "سياق تفاعلي"

يمكنك رمي هذا في mixin

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    return this.getReactiveData()
  })
componentWillUnmount: function() {
  this.comp.stop()
}

وبعد ذلك تبقى لديك هذه الوظيفة التفاعلية السحرية التي تعمل فقط!

getReactiveData: function() {
  let sub = Meteor.subscribe('messages')
  return {
    loading: !sub.ready(),
    messages: Messages.find().fetch()
  }
}

المقتفي رائع مثل هذا ...

ccorcos تبين أنني كنت مخطئًا إلى حد ما بشأن إلغاء الاشتراك التلقائي عند استخدام أحداث على غرار Tracker. ما زلت بحاجة إلى إيقاف المستمع في componentWillUnmount ، وإلا فسيستمر في الوصول إلى مصادر البيانات والاتصال بـ setState (ما لم تتحقق من isMounted() الذي تم إيقافه الآن).

في ملاحظة جانبية ، يمكنك إنشاء ديكورات أنيقة لجعل طرق المكونات تفاعلية. إليك بعض الأمثلة: [1] ، [2] . يشبه هذا:

export class Chat extends React.Component {
  <strong i="13">@reactive</strong>
  updateState () {
    this.setState({
      auth: auth.read(),
      messages: messages.read()
    })
  }

  /* ... */
}

Mitranim أنيق جدًا - أفضل الوظائف عالية المستوى بالرغم من ذلك. ؛)

sebmarkbagejimfb لقد كنت أتابع هذا الموضوع والخيط البديل (# 3858) لبضعة أشهر حتى الآن ، وأنا أشعر بالفضول إذا كان الفريق الأساسي قد توصل إلى أي إجماع حول هذه المشكلة ، أو على الأقل اتجاه عام.

oztune لا توجد تحديثات ؛ لقد ركزنا على أولويات أخرى. سنقوم بالنشر على أحد المواضيع عندما يكون هناك تحديث حول هذا الموضوع.

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

import { compose } from `react-komposer`;

// Create a component to display Time
const Time = ({time}) => (<div>{time}</div>);

// Create the composer function and tell how to fetch data
const composerFunction = (props, onData) => {
    const handler = setInterval(() => {
    const time = new Date().toString();
    onData(null, {time});
  }, 1000);

  const cleanup = () => clearInterval(handler);
  return cleanup;
};

// Compose the container
const Clock = compose(composerFunction)(Time);

// Render the container
ReactDOM.render(<Clock />, document.getElementById('react-root'));

ها هي النسخة الحية: https://jsfiddle.net/arunoda/jxse2yw8/

لدينا أيضًا بعض الطرق السهلة لإنشاء الحاويات باستخدام Promises و Rx.js Observables و Meteor's Tracker .

تحقق أيضًا من مقالتي حول هذا: دعونا نؤلف بعض حاويات التفاعل

arunoda انتهى بنا الأمر بفعل شيء مشابه جدًا. أحد الأشياء التي أتساءل عنها هو كيف تمنع استدعاء وظيفة الملحن عند كل تغيير في العنصر؟

oztune في الواقع الآن سيتم تشغيله مرة أخرى. نحن نستخدم هذا Lokka و Meteor. يحتوي كلاهما على ذاكرات تخزين مؤقت محلية ولا يصلان إلى الخادم حتى عندما نستدعي الوظيفة composerFunction عدة مرات.

لكنني أعتقد أنه يمكننا فعل شيء كهذا:

const options =  {propsToWatch: ["postId"]};
const Clock = compose(composerFunction, options)(Time);

أيه أفكار؟

arunoda هذا ما جربناه أيضًا ، لكنه يخلق نوعًا من الانفصال بين الإجراء وتبعياته. نفعل الآن شيئًا مشابهًا للتفاعل غير المتزامن ، حيث بدلاً من تنفيذ الإجراء المحدد على الفور ، ستعيد الدالة composerFunction وظيفة ومفتاحًا. إذا كان المفتاح مختلفًا عن المفتاح السابق الذي أرجعته وظيفة composerFunction ، فسيتم تنفيذ الوظيفة الجديدة. لست متأكدًا مما إذا كان هذا مماسًا من سلسلة رسائل جيثب هذه ، لذلك سأكون سعيدًا للمتابعة على Twitter (اسم المستخدم نفسه).

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

لماذا لا تدع JSX فقط لفهم وتقديم Observables مباشرة حتى أتمكن من تمرير Observable في الدعائم ، على سبيل المثال this.props.todo $ وتضمينها في JSX. بعد ذلك ، لن أحتاج إلى أي واجهة برمجة تطبيقات ، تتم إدارة الباقي خارج React ويتم استخدام HoC لملء العناصر المرئية. لا يهم ما إذا كانت الخاصيات تحتوي على بيانات عادية أو يمكن ملاحظتها ، وبالتالي لا توجد حاجة إلى هذه البيانات الخاصة.

{this.props.todo $

بالإضافة إلى ذلك ، يمكن أن يكون تصيير React قادرًا على عرض Oservable [JSX] مما يسمح بالتصميم الموصوف في الروابط بدون مكتبة إضافية.

https://medium.com/@milankinen/containers -are-dead-long-life-combinators-2cb0c1f06c96 # .yxns1dqin

https://github.com/milankinen/react-combinators

مرحبا،
حتى الآن يمكننا استخدام مكونات عديمة الحالة مع تدفقات rxjs بسهولة.
أنا لا أفهم الحاجة إلى واجهة برمجة تطبيقات أخرى.
لقد كتبت مثالًا - لوحة يمكنك تحريك الماوس فوقها وعندما تصل إلى 26 يتم تغييرها لإعادة التشغيل.
أحب أن أسمع ما هو رأيك بهذه الطريقة.
ها هو الكود:
https://jsfiddle.net/a6ehwonv/74/

giltig : أنا أتعلم بهذه الطريقة مؤخرًا أيضًا

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

يمكن أن تدعم RxJs الإضافية الكائن في

const myFancyReactComponent = ({surface, number, gameover}) => (
        <div> 
          {gameover ? gameover : surface}
          {number}
        </div>
)

const LiveApp = Rx.Observable.combineLatest(
    LiveSurface, DynamicNumberView, DynamicGameOver,
    myFancyReactComponent
)

مرحبا،
السبب في أننا لا نستخدم Cycle أو أي إطار عمل آخر هو أنني أفضل المكتبات على الأطر (مزيد من التحكم للمطور) وأريد أيضًا الاستمتاع بقوة المجتمع التي يتمتع بها React.
تم تطوير العديد من محركات التصيير الآن لـ React ومن المؤسف عدم استخدامها (ReactNative و ReactDom و ReactThree إلخ ..). من السهل جدًا استخدام Rxjs فقط والتفاعل كما هو موضح أعلاه.

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

راجع للشغل ما فعلته مع MyFancyReactComponent ممكن وقد فعلنا ذلك أيضًا في بعض الحالات على الرغم من أنه يمكنك أيضًا كتابة jsx مباشرة.

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

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

الدعائم التي يمكن ملاحظتها ليس لها معنى على المدى الطويل. لا معنى له على الإطلاق في هذا السياق ، في الواقع ..

يبدو لي أننا انتهينا من نموذج مختلف مع التشويق (ذاكرة التخزين المؤقت + السياق). قد تحصل ذاكرة التخزين المؤقت نفسها على دعم للاشتراكات. يمكنك تتبع العمل المتبقي للتشويق في https://github.com/facebook/react/issues/13206.

كما نقدم حزمة اشتراك لمزيد من الحالات المعزولة.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات