Redux: Лучшая асинхронная технология загрузки на стороне сервера?

Созданный на 15 июн. 2015  ·  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 обеспечивает полную синхронность хранилищ. Вместо этого то, что вы описываете, должно происходить в создателе действий.

Я _ думаю_, что это возможно даже с промежуточным программным обеспечением преобразователя по умолчанию. Создатель вашего действия будет выглядеть так:

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 Мне любопытно, как вы реализовали ожидание разрешения обещаний на сервере?

Это просто псевдокод, поэтому не вставляйте его куда-либо, но я использую реагирующий маршрутизатор (чей api изменяется так же быстро, как и 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));
      });
  });
});

Это что-то проясняет?

@iest

Цитата о вашей проблеме в 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 Что касается вашего последнего комментария, в котором вы вызываете метод fetchData для того, что у вас есть как 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: {} }

У вас не будет доступа к Бару в Маршрутах

@erikras Фантастика! Это именно тот путь, по которому я хочу пойти. Спасибо, что поделился.

@iest Я надеюсь, что этот каламбур был намеренным: «пойти по маршруту», перебирая совпадающие маршруты. :-)

@mattybow Это правда. Если вам действительно_ нужен компонент, которого нет в ваших маршрутах для загрузки чего-либо, то единственный вариант - запустить React.renderToString() один раз (отбросив результат), выполнить всю загрузку в componentWillMount() и как-то сохраняйте обещания на ходу. Это то, что я делал со своим собственным решением маршрутизации до того, как react-router поддерживал рендеринг на стороне сервера. Я бы предположил, что необходимость в компонентах, не являющихся маршрутами для загрузки, может быть признаком проблемы проектирования. В большинстве случаев маршрут знает, какие данные потребуются его компонентам.

@erikras
у вас есть публичное репо, чтобы увидеть там весь пример вашего решения?

@transedward Я бы хотел этого, но мои вещи, использующие метод, который я здесь подробно описал, все еще очень незрелые. Извините.

+1 на расширенном изоморфном примере
Мне нравится, к чему все идет!

@transedward Вот пример проекта со всеми передовыми технологиями, которые я сколотил. https://github.com/erikras/react-redux-universal-hot-example/

@erikras Это

Спасибо! PR отправлены.

@erikras Отлично - Спасибо!

Замечу, что - основываясь на некоторых идеях этого разговора - я создал промежуточное ПО для обработки обещаний: https://github.com/pburtchaell/redux-promise-middleware.

@pburtchaell Также есть эта библиотека от @acdlite . https://github.com/acdlite/redux-promise

Две мысли по этому поводу:

  1. Обещания, которые преобразуются в действия, могут быть переданы вместе с действием и помещены в Redux Store.

Затем, чтобы узнать, готова ли ваша страница к рендерингу, просто проверьте, все ли обещания выполнены. Возможно, используйте промежуточное ПО, которое связывает все проходящие обещания вместе и предоставляет обещание, когда нет ожидающих обещаний.

  1. Как насчет того, чтобы позволить селекторам вызывать действия при отсутствии данных?

Предположим, вы хотите отобразить сообщение 3, тогда ваш контейнер сообщений отобразит <Message id={3}> а селектор сообщений проверит, существует ли state.msgs[3] а если нет, отправит обещание загрузки сообщения.

Так что объедините два, и компоненты автоматически выберут свои необходимые данные, и вы узнаете, когда они будут выполнены.

Я почти уверен в том, что «не помещайте ничего несериализуемого в хранилище или действия». Это один из инвариантов, который очень хорошо работал у меня (например, позволял путешествовать во времени), и должны быть очень веские причины подумать об изменении его.

Затем, чтобы узнать, готова ли ваша страница к рендерингу, просто проверьте, все ли обещания выполнены. Возможно, используйте промежуточное ПО, которое связывает все проходящие обещания вместе и предоставляет обещание, когда нет ожидающих обещаний.

На самом деле для этого не нужно помещать обещания в магазин в конце, и мне это нравится. Разница в том, что в конце цепочки отправки необработанные действия не содержат никаких обещаний. Вместо этого они «собираются» упомянутым промежуточным программным обеспечением.

Одна вещь, которую я часто делаю при работе с обещаниями, - это держать ссылку на обещание, чтобы при поступлении других запросов на то же самое я просто возвращал то же обещание, обеспечивая устранение ошибок. Затем я удаляю ссылку через некоторое время после выполнения обещания, обеспечивая настраиваемое кеширование.

Мне действительно нужно начать использовать Redux в реальных приложениях, потому что мне интересно, что делать с этими ссылками в Redux. Я как бы хочу вставить их в хранилище, чтобы сделать actionCreator без состояния (или, по крайней мере, сделать состояние явным). Передача обещания через действие - хороший способ его экспортировать, но потом вам нужно как-то вернуть его обратно. Хм.

Я очень жду ответа на последний пункт @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());

это решает!
Какое-то время я думал (без всякой причины), что доступ к состоянию в actionCreator был плохой практикой ^^ так как вызывающий создатель действия может получить доступ к состоянию, я не понимаю, почему actionCreator не должен этого делать, так что да, круто, мы можем это сделать :)

Спасибо @gaearon

@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))
    );
  };
}

тогда не обязательно иметь общий обратный вызов ошибки для отказа получить секретный соус, так как могут быть разные обстоятельства для получения соуса.

Таким образом, я могу видеть модель преобразователя более гибкой.

Возможно, что-то вроде ведения журнала или, может быть, даже переключение «асинхронного индикатора занятости» являются более подходящими примерами промежуточного программного обеспечения?

@ justin808

Оба в порядке. Выберите, что менее многословно для вас и что лучше подходит для вашего проекта. Я предлагаю начать с использования преобразователей, и если вы видите повторяющийся шаблон, извлеките его в пользовательское промежуточное ПО. Их тоже можно смешать.

Я создал 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.

Wout.
(набрано на мобильном телефоне, извините за лаконичность)

На самом деле, в магазине я помещаю настраиваемые объекты Action, они просто Immutable.Record с простыми атрибутами (id, state, payload) и триггером действия, который создает и возвращает Promise, поэтому я не помещаю Promises в Store. Но я, наверное, где-то сломаюсь, Дже. Спасибо @wmertens.

@gabrielgiussi

и триггер действия, который создает и возвращает обещание

Не помещайте в состояние функции или что-либо еще, что не сериализуемо.

Извините. Я пытался сказать

и триггер функции, который создает и возвращает обещание

На самом деле я кладу в магазин объект Action (название не лучшее):

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 «s вопрос https://github.com/rackt/redux/issues/99#issuecomment -112980776 (вложенные компоненты управляющие выборки данных), но такой успех (не было уверен, как собирать обещания с componentWillMount ).

@chemoish Я также пытаюсь разобраться в рендеринге на стороне сервера с помощью react и redux. Пример в документации не очень хорошо справляется с этим вариантом использования. Я не хочу снова указывать и связывать каждый запрос API на сервере с моими компонентами. Компонент должен только указать, как получить необходимые данные (которые затем должен забрать сервер).

Ваши решения выглядят неплохо для этого. Это сработало для вас? Спасибо

Изменить: я прав, что "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, чтобы понять, о чем я говорю.

@chemoish

Я обхожу это, предотвращая выполнение кода выборки, если магазин уже заполнен

Хорошо, я понял. Спасибо.

Однако предложенное выше решение требует, чтобы каждая страница знала данные для всех своих дочерних компонентов.

Возможно, я неправильно понял ваше решение, но разве это не обязательное требование независимо от рендеринга на стороне сервера? (например, он должен отображать то же состояние, если я обновляю текущий маршрут, даже если это SPA)

Было бы, но вы можете захотеть, чтобы вложенный компонент управлял своим собственным извлечением данных по какой-либо причине.

Например, компонент, который повторяется на многих страницах, но для каждой страницы не требуется много данных.

@chemoish Я не уверен, что мы на одной странице. Позвольте мне попытаться объяснить свою точку зрения.

Например, у меня есть три вложенных компонента:

  • компонент1 (статический dataFetch1)

    • компонент2 (статический dataFetch2)

    • component3 (статический dataFetch3)

У каждого из них есть свои собственные методы "componentDidMount" с собственными объявлениями dataFetching (отправка действий через свой статический метод dataFetching).

Если у меня нет стороны сервера визуализации и я обновить текущий URL, мои компоненты будут монтировать и запускать все действия , необходимые для загрузки всех необходимых данных впоследствии.

При рендеринге на стороне сервера ваши match function и renderProps будут извлекать все три компонента, поэтому я могу получить доступ ко всем статическим методам dataFetching, которые затем позволят мне получить все данные, необходимые для начальный рендеринг на стороне сервера?

У вас есть ссылка на ваш match function из предоставленного вами примера? Спасибо.

@ ms88privat renderProps.components - это массив компонентов маршрутизатора, глубже он не идет. @chemoish означал, что с его реализацией вы не можете описать потребности в извлечении данных для более глубоких компонентов.

@DominicTobias thx, у вас есть решение этой проблемы? Есть ли возможность получить все вложенные компоненты?

Наверное, это поможет? https://github.com/gaearon/react-side-effect
Используется для сбора всех метатегов из вложенных элементов: https://github.com/nfl/react-helmet

Извините за повторное обсуждение этого обсуждения, но недавно я столкнулся с той же проблемой предварительного заполнения состояния с помощью асинхронного действия.

Я вижу, что @erikras переместил свой шаблонный проект на redux-async-connect . Интересно, а нашел ли кто-нибудь другое решение?

@vtambourine Я смотрел https://github.com/markdalgleish/redial, что очень полезно

Да, просмотрел. Но я не понял, как убедиться, что данные
извлечение подключенного кода не будет выполняться второй раз после повторной инициализации кода на n
клиент.
Пт, 18 марта 2016 г. в 22:54, Шон Мэтисон [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 Редукс-async-connect мертв? .

@vtambourine есть форк, который доступен по адресу https://github.com/makeomatic/redux-connect и поддерживается в хорошем состоянии. У него аналогичный API с небольшими изменениями, проверьте его, если вам интересно

для тех, кто заинтересован в решении redux с промежуточным программным обеспечением, как упомянуто

https://github.com/peter-mouland/react-lego-2016#redux -with-prom-middleware

Как при таком подходе создать создателя действий модульного тестирования?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги