Как увлажнять параметры на стороне сервера с помощью React + Redux

reactjs react-router react-redux react-router-redux

3868 просмотра

2 ответа

0 Репутация автора

У меня есть универсальное приложение React, которое использует Redux и React Router. Некоторые из моих маршрутов содержат параметры, которые на клиенте будут вызывать запрос AJAX для гидратации данных для отображения. На сервере эти запросы могут выполняться синхронно и обрабатываться по первому запросу.

Проблема, с которой я сталкиваюсь, заключается в следующем: ко времени, когда любой метод жизненного цикла (например componentWillMount) вызывается для маршрутизируемого компонента, уже слишком поздно отправлять действие Redux, которое будет отражено в первом рендере.

Вот упрощенное представление моего кода рендеринга на стороне сервера:

routes.js

export default getRoutes (store) {
  return (
    <Route path='/' component={App}>
      <Route path='foo' component={FooLayout}>
        <Route path='view/:id' component={FooViewContainer} />
      </Route>
    </Route>
  )
}

server.js

let store = configureStore()
let routes = getRoutes()
let history = createMemoryHistory(req.path)
let location = req.originalUrl
match({ history, routes, location }, (err, redirectLocation, renderProps) => {
  if (redirectLocation) {
    // redirect
  } else if (err) {
    // 500
  } else if (!renderProps) {
    // 404
  } else {
    let bodyMarkup = ReactDOMServer.renderToString(
      <Provider store={store}>
        <RouterContext {...renderProps} />
      </Provider>)
    res.status(200).send('<!DOCTYPE html>' +
      ReactDOMServer.renderToStaticMarkup(<Html body={bodyMarkup} />))
  }
})

Когда FooViewContainerкомпонент создается на сервере, его реквизиты для первого рендера уже будут исправлены. Любое действие, которое я отправляю в магазин, не будет отражено при первом обращении к нему render(), что означает, что они не будут отражены в том, что доставлено в запросе страницы.

idПараметр , который React маршрутизатор проходит вдоль не является, само по себе, полезно , что сначала делают. Мне нужно синхронно гидрировать это значение в нужный объект. Где я должен положить эту гидратацию?

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

Другое решение, которое я видел, заключается в добавлении статического fetchDataметода к каждому из компонентов контейнера в цепочке маршрутизатора. например что-то вроде этого:

FooViewContainer.js

class FooViewContainer extends React.Component {

  static fetchData (query, params, store, history) {
    store.dispatch(hydrateFoo(loadFooByIdSync(params.id)))
  }

  ...

}

server.js

let { query, params } = renderProps
renderProps.components.forEach(comp => 
  if (comp.WrappedComponent && comp.WrappedComponent.fetchData) {
    comp.WrappedComponent.fetchData(query, params, store, history)
  }
})

Я чувствую, что должен быть лучший подход, чем этот. Он не только выглядит довольно не элегантным ( .WrappedComponentнадежный интерфейс?), Но и не работает с компонентами более высокого порядка. Если какой-либо из перенаправленных классов компонентов будет обёрнут чем-то другим, connect()это перестанет работать.

Что мне здесь не хватает?

Автор: user5373139 Источник Размещён: 18.07.2016 02:02

Ответы (2)


0 плюса

0 Репутация автора

Кажется, нет более идиоматического способа сделать это, чем fetchDataподход, который я включил в свой первоначальный вопрос. Хотя это все еще кажется мне не элегантным, у него меньше проблем, чем я предполагал:

  • .WrappedComponentявляется стабильным интерфейсом, но ссылка все равно не нужна. Функция Redux connectавтоматически переносит любые статические методы из исходного класса в свою оболочку.
  • Любой другой компонент более высокого порядка, который оборачивает связанный с Redux контейнер, также должен поднимать (или пропускать) любые статические методы.

Могут быть и другие соображения, которых я не вижу, но я остановился на вспомогательном методе, подобном этому, в моем server.jsфайле:

function prefetchComponentData (renderProps, store) {
  let { params, components, location } = renderProps
  components.forEach(componentClass => {
    if (componentClass && typeof componentClass.prefetchData === 'function') {
      componentClass.prefetchData({ store, params, location })
    }
  })
}
Автор: user5373139 Размещён: 22.07.2016 08:46

1 плюс

2685 Репутация автора

Недавно я написал статью об этом требовании, но оно требует использования redux-sagas. Он выполняет раскладку с точки зрения редукс-thunks и использования этого статического шаблона fetchData / need.

https://medium.com/@navgarcha7891/react-server-side-rendering-with-simple-redux-store-hydration-9f77ab66900a

Я думаю, что этот сага-подход гораздо чище и проще рассуждать, но это может быть только мое мнение :)

Автор: nav Размещён: 01.08.2016 02:16
Вопросы из категории :
32x32