2015-08-28 8 views
6

Я бы хотел, чтобы мой SPA, основанный на React, отображался на стороне сервера (кто не в эти дни). Поэтому я хочу совместить React с react-router, redux и некоторым слоем сборки, например isomorphic starterkit.Как логически комбинировать реактивный маршрутизатор и редукцию для рендеринга на стороне клиента и на стороне сервера

Существует hapi universal redux, который объединяется вместе, но я борюсь с тем, как организовать мой поток. Мои данные поступают из нескольких конечных точек API REST. Различные компоненты имеют разные потребности в данных и должны загружать данные как раз на клиенте. Вместо этого на сервере должны быть получены все данные для определенного маршрута (набор компонентов) и необходимые компоненты, переданные в строки.

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

В некотором компоненте, который перечисляет несколько записей данных, имеется несколько ссылок для фильтрации записей. Каждый фильтрованный набор данных должен быть доступен через собственный URL-адрес, например /filter-by/:filter. Поэтому я использую разные компоненты <Link to={...}> для изменения URL-адреса при нажатии и запуска маршрутизатора. Маршрутизатор должен обновить хранилище, затем в соответствии с состоянием, представленным текущим URL-адресом, что, в свою очередь, вызывает повторную визуализацию соответствующего компонента.

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

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

So interaction -> URL change -> data fetching -> store update -> re-render.

Этот подход также должен работать на сервере, поскольку из запрошенного URL-адреса необходимо определить загружаемые данные, сгенерировать initial state и передать это состояние в генератор редукции store. Но я не нахожу способ правильно это сделать. Поэтому для меня возникают следующие вопросы:

  1. Является ли мой подход неправильным, потому что есть что-то, чего я еще не понимаю/не знаю?
  2. Правильно ли хранить данные, загруженные из API REST в ?
  3. Не слишком ли неудобно иметь компоненты, которые поддерживают state в редукции store и другие, управляющие своими state самими собой?
  4. Идея иметь interaction -> URL change -> data fetching -> store update -> re-render просто неправильно?

Я открыт для всех предложений.

ответ

3

Я установил точно то же самое сегодня. То, что мы уже имели, было реактивным маршрутизатором и редуктом. Мы модулировали некоторые модули, чтобы вводить в них вещи - и альта - это работает. Я использовал https://github.com/erikras/react-redux-universal-hot-example в качестве ссылки.

Части:

1. router.js

Мы возвращают function (location, history, store) настроить маршрутизатор с помощью обещаний. routes - это определение маршрута для маршрутизатора, содержащего все ваши компоненты.

module.exports = function (location, history, store) { 
    return new Bluebird((resolve, reject) => { 
     Router.run(routes, location, (Handler, state) => { 
      const HandlerConnected = connect(_.identity)(Handler); 
      const component = (
       <Provider store={store}> 
        {() => <HandlerConnected />} 
       </Provider> 
      ); 
      resolve(component); 
     }).catch(console.error.bind(console)); 
    }); 
}; 

2. store.js

Вы просто пройти начальное состояние в createStore(reducer, initialState). Вы просто делаете это на сервере и на клиенте. Для клиента вы должны сделать доступным состояние через тег скрипта (то есть window.__initialstate__). Для получения дополнительной информации см. http://rackt.github.io/redux/docs/recipes/ServerRendering.html.

3. рендеринг на сервере

Получит ваши данные, установить начальное состояние с этими данными (...data). createRouter = router.js свыше. res.render является экспресс-рендеринга шаблона нефрита со следующим

script. 
    window.csvistate.__initialstate__=!{initialState ? JSON.stringify(initialState) : 'null'}; 
... 
#react-start 
    != html 

var initialState = { ...data }; 
var store = createStore(reducer, initialState); 
createRouter(req.url, null, store).then(function (component) { 
    var html = React.renderToString(component); 
    res.render('community/neighbourhood', { html: html, initialState: initialState }); 
}); 

4. адаптируя клиенту

Ваш клиент может затем сделать в принципе то же самое. location может быть HistoryLocation от React-маршрутизатора

const initialState = window.csvistate.__initialstate__; 
const store = require('./store')(initialState); 

router(location, null, store).then(component => { 
    React.render(component, document.getElementsByClassName('jsx-community-bulletinboard')[0]); 
}); 

Чтобы ответить на ваши вопросы:

  1. Ваш подход кажется правильным. Мы делаем то же самое. Можно даже включить url как часть государства.
  2. Все состояние внутри магазина redux - хорошая вещь. Таким образом, у вас есть один источник истины.
  3. Мы по-прежнему разрабатываем то, что должно идти туда, где прямо сейчас. В настоящее время мы запрашиваем данные на componentDidMount на сервере, которые должны быть уже там.

 Смежные вопросы

  • Нет связанных вопросов^_^