gpt4 book ai didi

javascript - React 服务器端渲染 - 如何使用 :productId params passed in? 从服务器渲染

转载 作者:行者123 更新时间:2023-12-03 07:42:46 24 4
gpt4 key购买 nike

我有一个在客户端和服务器(节点和express)上呈现的React应用程序。如果有人输入 url http://webaddress.com/products/1,我会尝试让渲染正常工作。如果我输入此内容并按 Enter 键(或刷新页面),应用程序就会崩溃,因为它不知道如何获取 url 中的 1 来解析并获取正确的产品。

如果我单击导航中链接到 products/1 的链接,应用程序将正常工作并显示正确的产品。

如何让 React-Router 从访问者在 http://webaddress.com/products/1 中输入的 URL 中获取 :productsId (/products/1 中的 1)参数?

这是我的 server.js:

import express from 'express';
import http from 'http';

var PageNotFound = require('./js/components/PageNotFound.react');

import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';

import { routes } from './routes';

const app = express();

app.use(express.static('public'));

app.set('view engine', 'ejs');

/* We call match(), giving it the routes object defined above and the req.url, which contains the url of the request. */

app.get('*', (req, res) => {
// routes is our object of React routes defined above
match({ routes, location: req.url }, (err, redirectLocation, props) => {
if (err) {
// something went badly wrong, so 500 with a message
res.status(500).send(err.message);
} else if (redirectLocation) {
// we matched a ReactRouter redirect, so redirect from the server
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (props) {
// console.log("props on server.js: ", props);
// if we got props, that means we found a valid component to render
// for the given route. renderToString() from ReactDOM takes that RoutingContext
// component and renders it with the properties required.
const markup = renderToString(<RouterContext {...props} />);

// render `index.ejs`, but pass in the markup we want it to display
res.render('index', { markup })

} else {
// no route match, so 404. In a real app you might render a custom
// 404 view here
console.log("not found page");
res.sendStatus(404);
// respond with html page
if (req.accepts('html')) {
res.render('404', { url: req.url });
return;
}

// respond with json
if (req.accepts('json')) {
res.send({ error: 'Not found' });
return;
}

// default to plain-text. send()
res.type('txt').send('Not found');
}
});
});

const server = http.createServer(app);

app.set('port', (process.env.PORT || 3000))

app.get('/', (req, res) => {
var result = 'App is Running'
res.send(result);
}).listen(app.get('port'), () => {
console.log('App is running, server is listening on port', app.get('port'));
});

这是routes.js 文件:

import TopLevelContainerApp from './js/components/TopLevelContainerApp.react'
import Home from './js/components/Home.react';
import Product from './js/components/Product.react';

const routes = {
path: '',
component: SolidBroadheadApp,
childRoutes: [
{
path: '/',
component: Home
},
{
path: '/products/:productId',
component: Product
}
}
export { routes };

这是客户端渲染js:

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, browserHistory } from 'react-router';

import { routes } from './../routes';

ReactDOM.render(
<Router routes={routes} history={browserHistory} />, document.getElementById('website-app')
);

这是 ProductStore.js:

var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');

var articles = null;
var links = null;
var product = null;

function setArticles(receivedArticles) {
articles = receivedArticles;
return articles;
}

function setLinks(receivedLinks) {
links = receivedLinks;
return links;
}

function setProduct(productId) {
console.log("products store productId: ", productId);
function filterById(obj) {
return obj.id === productId;
}

var filteredArticlesArr = articles.filter(filterById);
product = filteredArticlesArr[0];
return product;
};

function emitChange() {
ProductsStore.emit('change');
}

var ProductsStore = assign({}, EventEmitter.prototype, {

addChangeListener: function(callback) {
this.on('change', callback);
},

removeChangeListener: function(callback) {
this.removeListener('change', callback);
},

getArticles: function() {
return articles;
},

getLinks: function() {
return links;
},

getProduct: function() {
return product;
}

});

function handleAction(action) {

switch (action.type) {

case 'received_products_articles':
setArticles(action.articles);
emitChange();
break;

case 'get_links':
setLinks(action.articles);
emitChange();
break;

case 'get_product':
setProduct(action.productId);
emitChange();
break;
}

}

ProductsStore.dispatchToken = AppDispatcher.register(handleAction);

module.exports = ProductsStore;

这是呈现特定产品的产品组件:

var React = require('react');
var ProductArticle = require('./products-solid-components/ProductArticle.react');

var ProductsStore = require('./../stores/ProductsStore');

// gets all the products
var ProductsArticlesWebUtilsAPI = require('./../../utils/ProductsArticlesWebUtilsAPI');
ProductsArticlesWebUtilsAPI.initializeArticles();

var Product = React.createClass({

getInitialState: function() {
return {
articles: ProductsStore.getArticles(),
product: ProductsStore.getProduct()
};
},


componentDidMount: function() {
ProductsStore.addChangeListener(this.onProductChange);
},

componentWillUnmount: function() {
ProductsStore.removeChangeListener(this.onProductChange);
},

onProductChange: function() {
this.setState({
articles: ProductsStore.getArticles(),
product: ProductsStore.getProduct()
});
},

render: function() {

if (this.state.articles)
if (this.state.product) {

return (
<div className="product">
<section className="container-fluid">
<ProductArticle name={this.state.product.name} largeImage={this.state.product.largeImage} description={this.state.product.description} />
</section>
);

} else {
return (
<div>Product is on the way</div>
);
}


}
});

module.exports = Product;

这是获取信息的文件:

var ProductsActionCreators = require('../js/actions/ProductsActionCreators');

var productArticles = [
{
"id": 1,
"name": "product 1",
"largeImage": "1.png",
"largeVideo": "1.mp4",
"description": "1 description",
},
{
"id": 2,
"name": "product 2",
"largeImage": "2.png",
"largeVideo": "2.mp4",
"description": "2 description",
},
{
"id": 3,
"name": "product 3",
"largeImage": "3.png",
"largeVideo": "3.mp4",
"description": "3 description",
},
];

var products = [];

function separateProductIdsAndNamesOut(productArticles) {
console.log("productArticles: " + productArticles);
products = productArticles.map(function(product) {
return { id: product.id, name: product.name };
});
return products;
}

function initializeArticles() {
return ProductsActionCreators.receiveArticles(productArticles);
}

// to build links in a nav component without payload of video and large img etc
function initializeProductsForNav() {
return ProductsActionCreators.receiveArticlesIdsAndNames(separateProductIdsAndNamesOut(productArticles));
}

module.exports = {
initializeArticles: initializeArticles,
initializeProductsForNav: initializeProductsForNav
};

更新:当我手动输入网址或在页面上点击刷新一次时,来自 server.js 的 console.log:

props on server.js:  { routes: 
[ { path: '', component: [Object], childRoutes: [Object] },
{ path: '/products/:productId', component: [Object] } ],
params: { productId: '4' },
location:
{ pathname: '/products/4',
search: '',
hash: '',
state: null,
action: 'POP',
key: '8b8lil',
query: {},
'$searchBase': { search: '', searchBase: '' } },
components:
[ { [Function] displayName: 'SolidBroadheadApp' },
{ [Function] displayName: 'Product' } ],
history:
{ listenBefore: [Function],
listen: [Function],
transitionTo: [Function],
push: [Function],
replace: [Function],
go: [Function],
goBack: [Function],
goForward: [Function],
createKey: [Function],
createPath: [Function],
createHref: [Function],
createLocation: [Function],
setState: [Function],
registerTransitionHook: [Function],
unregisterTransitionHook: [Function],
pushState: [Function],
replaceState: [Function],
isActive: [Function],
match: [Function],
listenBeforeLeavingRoute: [Function] },
router:
{ listenBefore: [Function: listenBefore],
listen: [Function: listen],
transitionTo: [Function: transitionTo],
push: [Function: push],
replace: [Function: replace],
go: [Function: go],
goBack: [Function: goBack],
goForward: [Function: goForward],
createKey: [Function: createKey],
createPath: [Function: createPath],
createHref: [Function: createHref],
createLocation: [Function: createLocation],
setState: [Function],
registerTransitionHook: [Function],
unregisterTransitionHook: [Function],
pushState: [Function],
replaceState: [Function],
__v2_compatible__: true,
setRouteLeaveHook: [Function: listenBeforeLeavingRoute],
isActive: [Function: isActive] } }
this.state.searchResult: null
this.state.productLinks in component: null

更新 2:我已删除 404 和 splat 路由,服务器 console.log 显示:

App is running, server is listening on port 3000
props.params on server.js: { productId: '4' } // looks good; proper productId is passed.
this.state.productLinks in component: null
this.props in product component: { productId: '4' } // looks good; should render properly
props.params on server.js: { productId: 'build.js' } // why is this assigned 'build.js' causing the problem???
this.state.productLinks in component: null
this.props in product component: { productId: 'build.js' } // why is this assigned 'build.js'? This is probably the cause of the problem.

该进程似乎运行了两次,第一次分配了正确的 id,第二次分配了由 webpack 构建的“build.js”? WTF。

显示 props 从 ProductId 重写为“build.js”的要点: https://gist.github.com/gcardella/1367198efffddbc9b78e

Webpack 配置:

var path = require('path');
var webpack = require('webpack');

module.exports = {
entry: path.join(process.cwd(), 'client/client-render.js'),
output: {
path: './public/',
filename: 'build.js'
},
module: {
loaders: [
{
test: /.js$/,
loader: 'babel'
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
APP_ENV: JSON.stringify('browser')
}
})
]
}

更新 3:我通过删除状态检查来修复双循环,但是在渲染服务器端时会失败,因为状态尚未设置。因此,我添加了一个检查来查看产品组件中是否存在 state.product:

var React = require('react');
var ProductArticle = require('./products-solid-components/ProductArticle.react');
var ProductPresentation = require('./products-solid-components/ProductPresentation.react');
var ProductLargeImage = require('./products-solid-components/ProductLargeImage.react');

var LargeLeftImageArticle = require('./reusable-tool-components/LargeLeftImageArticle.react');
var LargeRightImageArticle = require('./reusable-tool-components/LargeRightImageArticle.react');

var ProductsStore = require('./../stores/ProductsStore');

// if (process.env.APP_ENV === 'browser') {
var ProductsArticlesWebUtilsAPI = require('./../../utils/ProductsArticlesWebUtilsAPI');
ProductsArticlesWebUtilsAPI.initializeArticles();
// }

var Product = React.createClass({

getInitialState: function() {
return {
articles: ProductsStore.getArticles(),
product: ProductsStore.getProduct()
};
},


componentDidMount: function() {
ProductsStore.addChangeListener(this.onProductChange);
},

componentWillUnmount: function() {
ProductsStore.removeChangeListener(this.onProductChange);
},

onProductChange: function() {
this.setState({
articles: ProductsStore.getArticles(),
product: ProductsStore.getProduct()
});
},

render: function() {
console.log("this.props in product component: ", this.props);

// if (this.state.articles)

console.log("after check for this.state.product on component this.props.params.productId: ", this.props.params.productId);
if (this.state.product) {
return (
<div className="product">
<section className="container-fluid">
<ProductArticle name={this.state.product.name} largeImage={this.state.product.largeImage} description={this.state.product.description} />
</section>
</div>
);
} else {
console.log("no state");
return (
// if there is no state, how do I render something on the server side?
);
}
}
});

module.exports = Product;

最佳答案

事实证明这根本不是路由问题。不同领域存在很多问题:

我必须将 bundle.js 重命名为另一个名称,例如 client-bundle.js

然后我必须解决 ProductsStore 中的一个问题,以过滤产品数组以匹配 ProductId:

function setProduct(productId) {
var filteredProductsArr = products.filter(function(product) {
return product.id == productId;
});

product = filteredProductsArr[0];
return product;
};

然后我必须在我的产品组件的 componentDidMount() 中放置一个 Action 创建器:ProductsActionCreators.getProduct(this.props.params.productId);

所有这一切都像一个魅力:)

关于javascript - React 服务器端渲染 - 如何使用 :productId params passed in? 从服务器渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35338809/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com