Redux: أفضل تقنية تحميل غير متزامن على جانب الخادم؟

تم إنشاؤها على ١٥ يونيو ٢٠١٥  ·  63تعليقات  ·  مصدر: reduxjs/redux

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

أحاول استخدام redux لإنشاء تطبيق متماثل. إنها تعمل بشكل جميل حتى الآن ، باستثناء أنني بحاجة إلى معرفة كيفية الانتظار حتى يتم تحميل متاجري (على الخادم) قبل إعادة تحميل الصفحة الأولية. من الناحية المثالية ، يجب أن يتم التحميل في المتاجر نفسها ، ولكن عندما أتصل بـ dispatch(userActions.load()) ، يجب على المتجر إعادة الحالة الجديدة (على سبيل المثال ، return { ...state, loading: true }; ) ، لذلك لا يمكن إعادة الوعد إلى أنتظر لأجل. يُرجع dispatch() الإجراء الذي تم تمريره إليه لسبب ما. أود حقًا شيء مثل ...

dispatch(someAsyncAction, successAction, failureAction) => Promise

... حيث لا يتم حل الوعد حتى يتم إرسال أحد الإجراءين الآخرين.

هل هذا هو نوع الشيء الذي يمكن تمكينه باستخدام نمط البرمجيات الوسيطة؟

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

شكرا.

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

// Middleware
export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({ ...rest, type: REQUEST });
    return promise.then(
      (result) => next({ ...rest, result, type: SUCCESS }),
      (error) => next({ ...rest, error, type: FAILURE })
    );
  };
}

// Usage
function doSomethingAsync(userId) {
  return {
    types: [SOMETHING_REQUEST, SOMETHING_SUCCESS, SOMETHING_FAILURE],
    promise: requestSomething(userId),
    userId
  };
}

ال 63 كومينتر

مرحبًا ، شكرًا!

من الناحية المثالية ، يجب أن يتم التحميل في المتاجر نفسها

يفرض Redux أن المتاجر متزامنة تمامًا. ما تصفه يجب أن يحدث في منشئ الإجراء بدلاً من ذلك.

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

export function doSomethingAsync() {
  return (dispatch) => {
    dispatch({ type: SOMETHING_STARTED });

    return requestSomething().then(
      (result) =>  dispatch({ type: SOMETHING_COMPLETED, result }),
      (error) =>  dispatch({ type: SOMETHING_FAILED, error })
    );
  };
}

والتعامل مع الإجراءات الفعلية (الحبيبية) في المتجر.

من الممكن أيضًا كتابة برمجية وسيطة مخصصة لإزالة النموذج المعياري.

العبقري! اعتقدت أنني كنت أغفل شيئًا واضحًا. أحب هذا الفصل بين _العمل_ والتخزين_.

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

يمكنك أيضًا كتابة برنامج وسيط مخصص مثل هذا

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    next({ ...rest, readyState: 'request' );
    return promise.then(
      (result) => next({ ...rest, result, readyState: 'success' }),
      (error) => next({ ...rest, error, readyState: 'failure' })
    );
  };
}

واستخدمه بدلاً من الافتراضي.

سيتيح لك ذلك كتابة منشئي الإجراءات غير المتزامنة مثل

function doSomethingAsync(userId) {
  return {
    type: SOMETHING,
    promise: requestSomething(userId),
    userId
  };
}

وجعلهم يتحولون إلى

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, readyState: 'success' }
{ type: SOMETHING, userId: 2, readyState: 'failure' }

أوه ، هذا لطيف أيضًا ، وأكثر مما كان يدور في خلدي عندما طرحت السؤال الأصلي. لا يمكنني معرفة ما إذا كنت أحب المقايضة في تقليل عدد ثوابت الإجراء مقابل إضافة if s للتحقق من readyState داخل المتجر. أعتقد أنني قد أفضّل وجود إصدارات إضافية من _SUCCESS و _FAILURE من كل إجراء فقط لتجنب وضع if داخل case .

شكرا على كل حال.

نعم ، الأمر متروك تمامًا لذوقك. يمكن أن يكون لديك إصدار مماثل يحول types: { request: ..., success: ..., failure: ... } إلى إجراءات. هذا هو الهدف من جعله وسيطًا بدلاً من الخبز في المكتبة: لكل شخص ذوقه الخاص في هذه الأشياء.

// Middleware
export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({ ...rest, type: REQUEST });
    return promise.then(
      (result) => next({ ...rest, result, type: SUCCESS }),
      (error) => next({ ...rest, error, type: FAILURE })
    );
  };
}

// Usage
function doSomethingAsync(userId) {
  return {
    types: [SOMETHING_REQUEST, SOMETHING_SUCCESS, SOMETHING_FAILURE],
    promise: requestSomething(userId),
    userId
  };
}

يا رجل ، أنا أحب هذا الحل. أجمل بكثير من الحصول على then() والمكالمات الإضافية إلى dispatch() مثل الحل الأول الذي اقترحته. الصيحة للوسيطة!

اسمحوا لي أن أعرف كيف (وما إذا كان ؛-) يعمل!
لم نختبر البرامج الوسيطة المخصصة كثيرًا.

لقد تركت } (أي -1 نقطة) ، لكنها عملت مثل السحر! اول مرة.

: +1:

erikras أنا نفذتم انتظار الوعود بحل على الخادم؟

هذا مجرد كود زائف ، لذا لا تلصق هذا في أي مكان ، لكني أستخدم جهاز توجيه رد الفعل (الذي تتغير واجهة برمجة تطبيقاته بسرعة مثل redux) شيء من هذا القبيل:

app.get('/my-app', (req, res) => {
  Router.run(routes, req.path, (error, initialState) => {
    Promise.all(initialState.components
      .filter(component => component.fetchData) // only components with a static fetchData()
      .map(component => {
        // have each component dispatch load actions that return promises
        return component.fetchData(redux.dispatch);
      })) // Promise.all combines all the promises into one
      .then(() => {
        // now fetchData() has been run on every component in my route, and the
        // promises resolved, so we know the redux state is populated
        res.send(generatePage(redux));
      });
  });
});

هل هذا يوضح أي شيء؟

@ most

نقلا عن مشكلتك في Slack:

لدي معالج طريق مع

 static async routerWillRun({dispatch}) {
   return await dispatch(UserActions.fooBar());
 }

حيث UserActions.fooBar() هو:

export function fooBar() {
 return dispatch => {
   doAsync().then(() => dispatch({type: FOO_BAR}));
 };
}

ثم في عرض الخادم الذي أقدمه:

 yield myHandler.routerWillRun({dispatch: redux.dispatch});

لكنها لا تعمل.

أعتقد أن المشكلة هنا هي أنك لا تقوم فعليًا بإرجاع أي شيء من الطريقة المتداخلة fooBar .

قم بإزالة المشابك:

export function fooBar() {
  return dispatch =>
    doAsync().then(() => dispatch({type: FOO_BAR}));
}

أو أضف كشف حساب صريح return :

export function fooBar() {
  return dispatch => {
    return doAsync().then(() => dispatch({type: FOO_BAR}));
  };
}

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

erikras فيما يتعلق initialState.components (في رد نداء Router.run) ، فإن الكائن الذي تحصل عليه مراجع المكون من إرجاع معالجات التوجيه المتطابقة فقط. ما هي أفكارك حول الوصول إلى المكونات التي قد لا تكون معالج توجيه متطابق ، أي مكون فرعي ، ولكن يحتاج إلى جلب البيانات؟

هذا مثال على ما أتحدث عنه

import React from 'react';
import Router from 'react-router';
import {Route, RouteHandler, DefaultRoute} from 'react-router';

//imagine Bar needs some data
const Bar = React.createClass({
  render(){
    return(
      <div>bar</div>);
  }
});

const Foo = React.createClass({
  render(){
    return (
      <div>
        foo
        <Bar/>
      </div>);
  }
});


const App = React.createClass({
  render(){
    return (
      <div>
        <RouteHandler />
      </div>
    );
  }
});

const routes = (
  <Route path="/" handler={App} name="App">
    <DefaultRoute handler={Foo} name="Foo"/>
  </Route>
);

Router.run(routes,'/',function(Root,state){
  console.log(state);
});

انتاج:

{ path: '/',
  action: null,
  pathname: '/',
  routes: 
   [ { name: 'App',
       path: '/',
       paramNames: [],
       ignoreScrollBehavior: false,
       isDefault: false,
       isNotFound: false,
       onEnter: undefined,
       onLeave: undefined,
       handler: [Object],
       defaultRoute: [Object],
       childRoutes: [Object] },
     { name: 'Foo',
       path: '/',
       paramNames: [],
       ignoreScrollBehavior: false,
       isDefault: true,
       isNotFound: false,
       onEnter: undefined,
       onLeave: undefined,
       handler: [Object] } ],
  params: {},
  query: {} }

لن تتمكن من الوصول إلى شريط في المسارات

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

iest أتمنى أن يكون التورية متعمدًا ، "

mattybow هذا صحيح. إذا كنت تحتاج _ حقًا_ إلى مكون غير موجود في مساراتك لتحميل شيء ما ، فإن الخيار الوحيد هو تشغيل React.renderToString() مرة واحدة (تجاهل النتيجة) ، والقيام بكل ما تبذلونه من التحميل في componentWillMount() ، وبطريقة ما احفظ الوعود كما تذهب. هذا ما كنت أفعله مع حل التوجيه المحلي الخاص بي قبل react-router عرض جانب الخادم المدعوم. أود أن أقترح أن الحاجة إلى مكونات غير مرتبطة بالمسار للقيام بالتحميل قد تكون أحد أعراض مشكلة التصميم. في معظم حالات الاستخدام ، يعرف المسار البيانات التي ستحتاجها مكوناته.

تضمين التغريدة
هل لديك أي ريبو عام لترى مثالاً كاملاً على الحل الخاص بك هناك؟

transedward كنت أتمنى أن أفعل ذلك ، لكن أشيائي حتى الآن باستخدام الطريقة التي

+1 على المثال المتشابه المتقدم
أنا أحب إلى أين يذهب هذا!

transedward إليك نموذج مشروع مع كل https://github.com/erikras/react-redux-universal-hot-example/

erikras هذا رائع! هل يمكنك إرسال العلاقات العامة لإضافتها إلى قسم "مجموعات بدء التشغيل" في README و React Hot Loader؟

شكرا! تم إرسال PRs.

erikras العظمى - شكرا لك!

مجرد ملاحظة - بناءً على بعض الأفكار في هذه المحادثة - قمت بعمل برمجيات وسيطة للتعامل مع الوعود: https://github.com/pburtchaell/redux-promise-middleware.

pburtchaell توجد مكتبة من تصميمacdlite أيضًا. https://github.com/acdlite/redux-promise

فكرتان حول هذا:

  1. يمكن تمرير الوعود التي يتم تحويلها إلى أفعال جنبًا إلى جنب مع الإجراء ووضعها في متجر Redux.

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

  1. ماذا عن السماح للمحددات باستدعاء الإجراءات للبيانات المفقودة؟

لنفترض أنك تريد عرض الرسالة 3 ، فإن حاوية الرسالة ستعرض <Message id={3}> وسيتحقق محدد الرسالة من وجود state.msgs[3] وإذا لم يكن كذلك ، أرسل وعدًا بتحميل الرسالة.

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

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

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

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

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

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

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

هل يجب على مستخدم actionCreator (المكون) التحقق في كل مرة في الحالة إذا كان بإمكانه إرسال الإجراء أم لا؟ عليك أن تفعل ذلك في كل مرة ثم .. ربما تحتاج إلى تقديم التعليق التوضيحي الخاص بك connect الذي

لأنه لا يمكنك الوصول إلى حالة المتجر من خلال actionCreator

لماذا ا؟ يمكنك القيام بذلك على ما يرام إذا كنت تستخدم برمجيات thunk الوسيطة .

function doSomething() {
  return { type: 'SOMETHING' };
}

function maybeDoSomething() {
  return function (dispatch, getState) {
    if (getState().isFetching) {
      return;
    }

    dispatch(doSomething());
  };
}

store.dispatch(maybeDoSomething());

هذا يحلها!
اعتقدت لبعض الوقت (بدون سبب) أن الوصول إلى الحالة أثناء العمل كان منشئ المحتوى ممارسة سيئة ^ ^ بما أن المتصل منشئ الإجراء يمكنه الوصول إلى الحالة ، لا أرى سببًا لعدم قيام منشئ الإجراء بذلك ، لذا حسنًا ، يمكننا القيام بذلك :)

شكرا جايرون

gaearon ، هي تقنية استخدام Thunk والإجراءات المميزة http://gaearon.github.io/redux/docs/api/applyMiddleware.html أفضل من إجابتك أعلاه:

يمكنك أيضًا كتابة برنامج وسيط مخصص مثل هذا

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    next({ ...rest, readyState: 'request' );
    return promise.then(
      (result) => next({ ...rest, result, readyState: 'success' }),
      (error) => next({ ...rest, error, readyState: 'failure' })
    );
  };
}

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

function doSomethingAsync(userId) {
  return {
    type: SOMETHING,
    promise: requestSomething(userId),
    userId
  };
}

وجعلهم يتحولون إلى

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, readyState: 'success' }
{ type: SOMETHING, userId: 2, readyState: 'failure' }

أيضا،

أعتقد أنك كنت تقصد في الجزء الأخير:

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, result, readyState: 'success' }
{ type: SOMETHING, userId: 2, error, readyState: 'failure' }

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

في مثال thunk الخاص بك:

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

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

وبالتالي أستطيع أن أرى نموذج thunk أكثر مرونة قليلاً.

ربما شيء مثل التسجيل أو ربما حتى تبديل "مؤشر مشغول غير متزامن قيد التقدم" هي أمثلة أكثر ملاءمة للبرمجيات الوسيطة؟

هههههههههههههه

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

لقد قمت بإنشاء ActionStore لفصل حالة الإجراءات التي تم تشغيلها (تحميل ، نجح ، فشل) عن الحالة الأخرى. لكن لا أعرف ما إذا كان هذا يتعارض مع أساس Redux / Flux. لقد نشرت في stackoverflow حول هذا الموضوع.

gabrielgiussi أعتقد أن https://github.com/acdlite/redux-promise يمكنه أيضًا تحقيق ما تريد دون الحاجة إلى تخزين الوعود في الولاية. من المفترض أن تكون الدولة قابلة للتسلسل في جميع الأوقات.

wmertens شكرا على النصيحة. سألقي نظرة على الريبو ولكن لماذا لن تكون حالتي قابلة للتسلسل؟ أو تقولها فقط لأني أحيط علما؟

gabrielgiussi لم أنظر عن كثب لكن بدا الأمر كما لو كنت كذلك
وضع الوعود أو المهام في المتجر. على أي حال ، هذا المشروع
يجب أن تعمل بشكل جيد على ما أعتقد.

في الإثنين ، 10 أغسطس 2015 ، الساعة 19:15 كتب gabrielgiussi [email protected] :

wmertens https://github.com/wmertens شكرا على النصيحة. انا سوف
ألق نظرة على الريبو ولكن لماذا لا تكون حالتي قابلة للتسلسل؟ او انت
أقولها فقط لأني أحيط علما؟

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

ووت.
(مكتوب على الهاتف المحمول ، توجع العذر)

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

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

ومشغل إجراء يُنشئ وعدًا ويعيده

لا تضع وظائف أو أي شيء آخر غير قابل للتسلسل في الحالة.

آسف. حاولت أن أقول

ومشغل دالة يقوم بإنشاء وعد وإرجاعه

ما أضعه بالفعل في المتجر هو كائن إجراء (لم يكن الاسم هو الأفضل):

export default class Action extends Immutable.Record({state: 'idle', api: null, type: null, payload: null, id: null}){
load(){
  return this.set('state','loading');
}

succeed(){
  return this.set('state','succeeded');
}

fail(){
  return this.set('state','failed');
}

ended(){
  return this.get('state') != 'loading' && this.get('state') != 'idle';
}

endedWithSuccess(){
  return this.get('state') == 'succeeded';
}

endedWithFailure(){
  return this.get('state') == 'failed';
}

trigger() {
  return (dispatch) => {
    dispatch({type: this.get('type') + '_START', action: this});
    let payload = this.get('payload');
    this.get('api').call({dispatch,payload}).then((result) => {
      dispatch({type: this.get('type') + '_SUCCESS',id: this.get('id'), result: result.result});
    }).catch((result) => {
        dispatch({type: this.get('type') + '_FAIL',id: this.get('id'), result: result.result});
      });
  }
}
}

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

gaearon هذا الكود الذي كتبته https://github.com/rackt/redux/issues/99#issuecomment -112212639 ،

هل هذا شيء تم تضمينه في مكتبة redux أم أنه شيء يجب أن أقوم بإنشائه يدويًا؟ آسف إذا كان هذا سؤالًا جديدًا ، فما عليك سوى الدخول في React / Flux (Redux). بدأت للتو هذا البرنامج التعليمي https://github.com/happypoulp/redux-tutorial

@ banderson5144

لا يتم تضمينه. إنه موجود فقط لإعطائك فكرة عما يمكنك القيام به - لكن لك الحرية في القيام بذلك بشكل مختلف.
تم نشر شيء مشابه https://github.com/pburtchaell/redux-promise-middleware.

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

  • يمكنك إعادة تعيين حالة المتجر لمستخدم جديد
  • انتظر حتى تكتمل بعض الإجراءات غير المتزامنة قبل إعطاء المستخدم المتجر و html
  • يكتمل إجراء غير متزامن قيد التشغيل سابقًا لمستخدم آخر ويتلوث متجرك

كيف حللتم ذلك يا رفاق / لديكم أي أفكار كيف؟ مجرد متجر جديد لكل مستخدم سيعمل بدلاً من ذلك؟

هل تتحدث عن تقديم الخادم؟ إنشاء متجر جديد عند كل طلب. لدينا دليل لتقديم الخادم في المستندات.

شكرا سأفعل ذلك

بعد محاولة فهم ...

هل هذا ساذج جدا؟ (لا يبدو أن أي شخص آخر يفعل ذلك - على ما أعتقد)

// server.js
app.use(function (req, res) {
    match({…}, function (error, redirectLocation, renderProps) {
        …

        if (renderProps) {
            const store = configureStore();

            const promises = renderProps.components.map(function (component, index) {
                if (typeof component.fetchData !== 'function') {
                    return false;
                }

                return component.fetchData(store.dispatch);
            });

            Promise.all(promises).then(function () {
                res.status(200).send(getMarkup(store, renderProps));
            });
        }
    })
});
// home.js
export class Home extends Component {
    static fetchData() {
        return Promise.all([
            dispatch(asyncAction);
        ]);
    },

    componentDidMount() {
        const { dispatch } = this.props;

        Home.fetchData(dispatch);
    }
}

export default connect()(Home);
// action.js
export function asyncAction() {
    return (dispatch, getState) => {
        dispatch(request);

        return fetch(…)
            .then(response => response.json())
            .then(data => dispatch(requestSuccess(data)))
        ;
    }
}

كنت أحاول أيضًا اكتشاف حل لسؤالmattybow https://github.com/rackt/redux/issues/99#issuecomment -112980776 (المكونات المتداخلة التي تدير جلب البيانات) ، ولكن لم يكن هذا النجاح تأكد من كيفية جمع الوعود من componentWillMount ).

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

تبدو الحلول الخاصة بك جيدة جدًا لتحقيق ذلك. هل نجح هذا معك؟ شكرا

تحرير: أنا محق في أن "componentDidMount" لا يتم تشغيله مرة أخرى على العميل عند تقديمه على الخادم؟

@ ms88privat لم أحصل على الكثير من التعليقات حول الحل بعد ، ولم أختبر حدوده.

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

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


سيتم تشغيل componentDidMount مرة أخرى (راجع https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount). يمكنك استخدام طريقة دورة الحياة هذه أو طريقة أخرى تناسب احتياجاتك.

يمكنني التغلب على هذا من خلال منع تنفيذ رمز fetch إذا كان المتجر ممتلئًا بالفعل (أو أي منطق عمل تراه مناسبًا).

افحص https://github.com/reactjs/redux/blob/master/examples/async/actions/index.js#L47 للحصول على فكرة عما أتحدث عنه.

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

يمكنني الالتفاف حول هذا عن طريق منع تنفيذ رمز الجلب إذا كان المتجر ممتلئًا بالفعل

حسنًا ، لقد فهمت ذلك. شكرا.

ومع ذلك ، فإن الحل المطروح أعلاه يتطلب من كل صفحة معرفة البيانات لجميع مكوناتها الفرعية.

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

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

على سبيل المثال ، مكون يتكرر في العديد من الصفحات ، لكن كل صفحة لا تحتوي على الكثير من احتياجات جلب البيانات.

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

على سبيل المثال ، حصلت على ثلاثة مكونات متداخلة:

  • المكون 1 (البيانات الثابتة جلب 1)

    • العنصر 2 (بيانات ثابتة Fetch2)

    • العنصر 3 (بيانات ثابتة Fetch3)

كل واحد منهم لديه طرق "componentDidMount" الخاصة به ، مع وجود بيانات جلب البيانات الخاصة به (إرسال الإجراءات عبر طريقة dataFetching الثابتة الخاصة به).

إذا لم يكن لدي عرض جانبي وقمت بتحديث عنوان URL الحالي ، فسيتم تحميل مكوناتي وتشغيل جميع الإجراءات اللازمة لتحميل جميع البيانات المطلوبة بعد ذلك.

من خلال التقديم من جانب الخادم ، ستقوم دالة match و renderProps باستخراج المكونات الثلاثة جميعها ، حتى أتمكن من الوصول إلى جميع طرق جلب البيانات الثابتة ، والتي ستسمح لي بعد ذلك بإحضار جميع البيانات المطلوبة لـ التقديم الأولي من جانب الخادم؟

هل لديك مرجع إلى match function من المثال الذي قدمته؟ شكرا.

@ ms88privat renderProps.components عبارة عن مجموعة من مكونات جهاز التوجيه ، ولا تتعمق أكثر من ذلك. يعني chemoish أنه من خلال تنفيذه ، لا يمكنك وصف احتياجات جلب البيانات على المكونات الأعمق.

DominicTobias thx ، هل لديك حل لهذه المشكلة؟ هل هناك إمكانية للحصول على جميع المكونات المتداخلة؟

ربما هذا يمكن أن يساعد؟ https://github.com/gaearon/react-side-effect
تُستخدم لتجميع كل العلامات الوصفية من العناصر المتداخلة: https://github.com/nfl/react-helmet

آسف لمفاجأة هذه المناقشة مرة أخرى ، لكنني واجهت مؤخرًا نفس المشكلة المتمثلة في ملء الحالة مسبقًا بإجراء غير متزامن.

أستطيع أن أرى أن erikras نقل مشروعه المعياري لإعادة الاتصال غير المتزامن . أتساءل ، إذا وجد أحدهم حلاً آخر؟

vtambourine لقد كنت أبحث في https://github.com/markdalgleish/redial وهو أمر مفيد للغاية

نعم ، لقد بحثت فيها. لكنني لم أحصل على كيفية التأكد من البيانات
لن يتم تشغيل جلب hooked للمرة الثانية بعد رمز التهيئة إلى n
زبون.
في يوم 18، марта 2016 г. الساعة 22:54 ، Sean Matheson [email protected]
كتب:

vtambourine https://github.com/vtambourine لقد كنت أبحث في
https://github.com/markdalgleish/redial وهو أمر مفيد للغاية

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

من الغريب أيضًا أن يجد أي شخص حلاً مستقرًا لهذا التحدي. أنا أحب نموذجerikras ، ولكن كما ذكر vtambourine ، فقد انتقل إلى redux-async-connect الذي يبدو أنه قد لا يكون حلاً مستقرًا على المدى الطويل: # 81 هل توقف إعادة الاتصال غير المتزامن؟ .

vtambourine ، هناك شوكة متوفرة على https://github.com/makeomatic/redux-connect وتتم صيانتها جيدًا. لديها واجهة برمجة تطبيقات مشابهة مع بعض التغييرات عليها ، تحقق منها إذا كنت مهتمًا

للمهتمين بحل إعادة الإرسال باستخدام البرامج الوسيطة كما ذكر

https://github.com/peter-mouland/react-lego-2016#redux- مع الوعد والبرمجيات الوسيطة

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

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

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

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

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

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

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

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