Как сделать компонент для отображения только после завершения вызова API? - PullRequest
1 голос
/ 06 февраля 2020

У меня есть 1-летнее приложение React, которое использует рендеринг на стороне сервера, и сейчас мы разрабатываем страницу, которая будет проиндексирована роботом Googlebot.

Проблема в том, что нам нужен ответ asyn c API-вызова, чтобы отобразить страницу в этих данных для целей SEO. У Googlebot (view-page-source) не должно быть компонента Loading... или чего-либо еще. Его содержание ДОЛЖНО быть данными API.

Однако все примеры / решения, которые я нашел по этому поводу, указали использовать componentDidMount, componentWillMount (устарело) или даже конструктор, но все они сначала отображают страницу без данных, и робот Google не будет ждать ее Фини sh.


Пример:

Ответ API:

{ trip: { description: 'It was great', title: 'The Trip! 2.0' } } 

Компонент:


class HomePage extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      data: null,
    }
  }

  render() {
    return (
      <div>
        {
          this.state.data ? <h1>{this.state.data.title}</h1> : <p>none</p>
        }
      </div>
    );
  }
};

export default HomePage;

Требуемый исходный код для всех рендеров:

<h1>The Trip! 2.0</h1>

НИКОГДА: <p>none</p>


PS: вызов API не моделируется в этом примере, потому что idk куда его ставить = x

Что я мог сделать, чтобы решить эту проблему, учитывая, что компонент НЕ должен отображаться без ответа API? Это возможно? Спасибо всем!

1 Ответ

0 голосов
/ 14 марта 2020

Внутри каждого компонента вы должны определить функцию, назовем ее loadData. эта функция будет выполнять асин c работу вашего компонента. когда ваш сервер получает запрос, вы должны посмотреть этот URL, а затем решить, какие компоненты отображать.

Для каждого компонента, который необходимо отобразить, мы будем вызывать функцию loadData, которая прикреплена к каждому из компонентов, чтобы инициировать процесс загрузки данных. Ключевым моментом здесь является то, что мы не делаем начальный рендеринг приложения. У нас просто есть набор компонентов. Каждый говорит: «Вот ресурс, который мне нужен». Затем всякий раз, когда кто-то делает запрос, мы смотрим на набор компонентов, которые нам нужно визуализировать, чтобы показать страницу. Затем мы возьмем все эти компоненты, возьмем эти маленькие функции требований к загрузке данных, которые прикреплены к ним, и мы вызовем каждый из них. Все эти функции dataLoad возвращают обещание, поэтому мы должны обнаружить, что все они решены, прежде чем мы представим наше приложение. Допустим, у нас есть Users. js, поэтому мы определяем нашу функцию следующим образом:

const loadData = store => {
  return store.dispatch(fetchUsers());
};

, когда мы импортируем наш компонент, мы импортируем внутри объекта.

export default {
  loadData:loadData,
  component: connect(mapStateToProps, { fetchUsers })(UsersList)
};

Мы должны настроить наши маршруты. js файл с помощью реагирующий маршрутизатор-config .

Маршруты. js

import React from "react";
import Home from "./pages/Home";
import Users from "./pages/UsersList";
import App from "./App";
import NotFoundPage from "./pages/NotFoundPage";

export default [
  {
    ...App,
    //App component will be shown inside each component.
    //array of routes will be used inside the App.js down below
    routes: [
      {
        path: "/",
        ...Home,
        exact: true
      },
      { ...UsersList, path: "/users" },
      { ...AdminsListPage, path: "/admins" },
      { ...NotFoundPage }
    ]
  }
];

Вот приложение. js

import React from "react";
import Header from "./components/Header";
import { renderRoutes } from "react-router-config";
import { fetchCurrentUser } from "./actions";

//this component is to render components that each component uses in common
//props.route is the child components that are passed from Routes.js
const App = ({ route }) => {
  return (
    <div>
      <Header />
      {renderRoutes(route.routes)}
    </div>
  );
};

export default {
  component: App,
  //this function is get called by redux so store is passed in
  //as you might know Header should always know about authentication status
  //we populate the store with the authentication info so Header can use it.
  loadData: ({ dispatch }) => dispatch(fetchCurrentUser())
};

Теперь мы настроили все функции loadData, теперь пришло время чтобы вызвать их, когда наш сервер получает запрос. для этого мы будем использовать функцию matchRoutes из response-router-config.

app.get("*", (req, res) => {
  const store = createStore(req);

//express does not touch routing. it delegates everything to react.
//I will just place the logic behind invoking loadData functions here.
//matchRoutes will look at the path and will return the array of components to be loaded. 
//this is what matchRoutes function show `{ route: { loadData: [Function: loadData], path: '/users', component: [Object] },//component that we show
match: { path: '/users', url: '/users', isExact: true, params: {} } }
`
//
const promises = matchRoutes(Routes, req.path)
    .map(({ route }) => {
      return route.loadData ? route.loadData(store) : null;
    }) //we got the array of loadData functions now we are invoking them
    .map(promise => {
      if (promise) {
        return new Promise((resolve, reject) => {
          promise.then(resolve).catch(resolve);
        });
      }
    });
//Promise.all takes an array of promises and resolves when all of the items resolve
Promise.all(promises).then(()=>{
//here is when it is time to render your server-side code.

}).catch(e => console.log(e.message));


}
...