(Изоморфный) Внутренний рендеринг маршрутов и компонентов реагирования на основе ajax - PullRequest
0 голосов
/ 02 февраля 2019

Я пытаюсь создать приложение изоморфного реагирования.Я использую экспресс в качестве бэк-энда.У меня есть статический маршрутизатор с компонентом приложения в нем.Я ловлю любой запрос и использую renderToString следующим образом:

(приведенный ниже код неполон и может иметь синтаксические ошибки - не запускался через IDE)

server.jsx:

app.get("/*", (req, res) => {
 const itemIDs =
 ['myFirstItemID', 'mySecondItemID', 'myThirdItemID'] // fetched from DB

 const renderedApp = 
 ReactDOM.renderToString(<StaticRouter> <App itemIDs=itemIDs /> </StaticRouter>);

 res.render("index", { renderedData: renderedApp });
 res.end();
}

App.jsx - только метод визуализации

render() {
    const renderedRoutes = this.props.itemIDs.map(itemID => (<Route path={itemID} render={() => <Item ItemID={itemID}/>} />)
    );
        return (
                    <Switch>
                        {renderedRoutes}
                        <Route component={ItemsContainer itemIDs=itemIDs} />
                    </Switch>
        )
    }

ItemsContainer.jsx - только метод визуализации

render() {
      const ItemSummaries = this.props.itemIDs.map(itemId => <ItemPreview itemId={itemId}/>)

      return (
        <div>
            {ItemSummaries}
          </div>
      )

ItemPreview.jsx - только метод визуализации

 render() {
    return (
        <div>
          <Link to={"/" + this.props.itemId}>
        <h2>an item</h2>
    </Link>
        </div>
    )

Item.jsx - componendDidMount и рендер

componentDidMount() {
axios.get("/api/items?itemID=" + this.props.itemID)
.then((data) => this.state.itemData = data);

render() {
    return (
        <div>
          //use itemData here
    </Link>
        </div>
    )

Client.jsx - обслуживается клиенту (использует тот же компонент App.jsx)

const MyRoutedApp = () =>
        (<BrowserRouter>
            <App />
        </BrowserRouter>);

ReactDOM.hydrate(<MyRoutedApp/>, document.getElementById('root'));

Я чувствую, что что-то очень неправильно вмой подход, но я не уверен, что.У меня есть следующие проблемы:

  • ItemIDs используются только на сервере для генерации маршрутов.Как только приложение становится доступным на клиенте, у меня больше нет маршрутов, поэтому BrowserRouter не работает.Я могу получить маршруты, используя ajax, но это приводит к тому, что первоначальный рендеринг происходит до того, как у меня есть необходимые данные, что приводит к тому, что всегда отображается ItemsContainer (маршрут по умолчанию) для нескольких кадров независимо от того, по какому URL я нахожусь.
  • Какие у меня есть варианты, чтобы сделать renderToString компонентом, который полагается на ajax в componentDidMount для определения его содержимого render () (Item.jsx)?
  • И последнее, но не менее важное - действительно ли это хороший подход??

1 Ответ

0 голосов
/ 09 февраля 2019

Таким образом, я нашел способ решить первую проблему - визуализировать маршруты в исходном ответе страницы индекса (в теге <script>), а затем с помощью конструктора приложения обработать маршруты в зависимости от того, где они отображаются:

  • если визуализируется сервером - взять пропущенные реквизиты
  • если визуализировать в браузере - взять глобальный объект.

Вот как это будет выглядетьв коде:

Где-то на странице индекса (шаблон EJS):

//
<script>const globalRoutes = <%- JSON.stringify(renderedRoutes) %> </script>
//

"/" Обработчик запроса:

var fetchedRoutes; //your routes
app.get("/*", (req, res) => {

    var context = {};
    const routedApp = ReactDOMServer.renderToString(
        React.createElement(
            StaticRouter,
            { location: req.url, context: context },
            React.createElement(App, {routesData: fetchedRoutes})
  )
      );

    res.render("index", { renderedData: routedApp, renderedRoutes: fetchedRoutes });
    res.end();

});

Как приложение обрабатывает маршруты:

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = { routesData: this.isBrowser() ? globalRoutes : this.props.routesData }
    }
...