Мне было интересно, если у кого-то еще возникла проблема, когда он создал приложение NodeJS для рендеринга на стороне сервера, которое прекрасно работает локально, но не загружает серверную часть после развертывания на heroku.
Я создал приложение, используя потрясающий RazzleJS Джареда Палмера в сочетании с Redux, React Router и React Rouig Config.
Способ работает так, что в моем файле server.js
я проверяю компонент, который загружается, для статической функции с именем fetchData
, если функция существует, то функция запускается, что является запросом на основе обещаний к API, используя Аксиозы в гром.
В моем файле server.js
запускается другая функция, которая проверяет выполнение всех обещаний, прежде чем окончательно отобразить HTML-код для страницы.
Локально это работает отлично, и даже если Javascript отключен, страница загружается вместе с выбранными данными.
Развернув это на heroku (единый план dyno - hobby), однако, если я отключу javascript, страница загружается с отсутствующими данными, что предполагает, что страница отображается перед разрешением обещаний. Затем данные загружаются правильно с использованием эквивалентной отправки ComponentDidMount для данных.
В настоящее время у меня есть следующий код:
Server.js
function handleRender(req, res) {
const sheet = new ServerStyleSheet();
const store = createStore(rootReducer, compose(applyMiddleware(thunk)));
const branch = matchRoutes(Routes, req.url);
const promises = branch.map(({ route }) => {
let fetchData = route.component.fetchData;
return fetchData instanceof Function
? fetchData(store, req.url)
: Promise.resolve(null);
});
return Promise.all(promises).then(() => {
const context = {};
const html = renderToString(
sheet.collectStyles(
<Provider store={store}>
<StaticRouter context={context} location={req.url}>
{renderRoutes(Routes)}
</StaticRouter>
</Provider>
)
);
const helmet = Helmet.renderStatic();
const styleTags = sheet.getStyleTags();
const preloadedState = store.getState();
if (context.url) {
res.redirect(context.url);
} else {
res
.status(200)
.send(renderFullPage(html, preloadedState, styleTags, helmet));
}
});
}
Пример компонента React
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchProductData } from '../thunks/product-data';
class Test extends Component {
static fetchData(store, url) {
store.dispatch(fetchProductData());
}
componentDidMount() {
if(this.props.productData.length === 0 ) {
this.props.fetchProductData() // Successfully fetches the data
}
}
render() {
return (
<div>
{ this.props.productData && this.props.productData.map( (product, i) => {
return <div key={i}>{product.title}</div>
})}
</div>
);
}
}
const mapStateToProps = state => {
return {
productData: state.productData
}
};
const mapDispatchToProps = dispatch => {
return {
fetchProductData(){
dispatch(fetchProductData());
}
}
};
export const TestContainer = connect(mapStateToProps, mapDispatchToProps)(Test);
Это всего лишь пример компоновки компонентов, так как те, которые у меня есть, довольно сложны, но в этом случае productData
будет установлен в []
в defaultState.
Кроме того, все редукторы и действия работают правильно локально, и только при развертывании в Heroku по плану хобби рендеринг на стороне сервера больше не работает?