React Router Dom的服务器端呈现出现错误:警告:React.createElement:类型无效--应为字



精确错误:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for co
mposite components) but got: object.
in Posts
in Connect(Posts)
in Route
in Switch
in div
in App
in Route
in Switch
in div
in Router
in StaticRouter
in Provider

我在使用react路由器4进行服务器端渲染时出错。这个错误并没有真正指向任何特定的东西,但我认为这来自于我如何使用路由和导出组件。我将发布此问题的相关代码。

对于客户端代码,我将发布一个组件容器,因为我以相同的方式export所有组件容器。我只是想让你知道这是怎么做的。组件本身工作得很好,所以我将在底部发布导出代码。

注意:这是一个完整的演示项目(https://github.com/tbaustin/demo-SSR-RR4)我也用同样的方法。我还使用turbo,这是一种用于反应/还原的支架类型的系统。您将看到app.js看起来与普通的express应用程序有点不同,但我确保它的工作原理相同。

服务器:

app.js:

require('babel-core/register')({
presets: ['env', 'react', 'stage-0', 'stage-1']
});
const pkg_json = require('./package.json');
const vertex = require('vertex360')({ site_id: pkg_json.app });
var renderer = require('./renderer.js');
// initialize app
const app = vertex.app();
// import routes
const index = require('./routes/index');
const api = require('./routes/api');
// hopefully will be used on every Route, this should handle SSR RR4
app.use(renderer);
// set routes
app.use('/', index);
app.use('/api', api); // sample API Routes
module.exports = app;

render.js:

import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import serialize from 'serialize-javascript';
import { Helmet } from 'react-helmet';
import { matchRoutes } from 'react-router-config';
import routes from './src/routes';
import createStore from './src/stores';
function handleRender(res, req) {
const store = createStore.configure(null); // create Store in order to get data from redux
const promises = matchRoutes(routes, req.path)
.map(({ route }) => {
// Matches the route and loads data if loadData function is there
return route.loadData ? route.loadData(store) : null;
})
.map(promise => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve); // lets all data load even if route fails
});
}
});
Promise.all(promises).then(() => {
const context = {};
if (context.url) {
return res.redirect(301, context.url); // redirect for non auth users
}
if (context.notFound) {
res.status(404); // set status to 404 for unknown route
}
const content = renderToString(
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<div>{renderRoutes(routes)}</div>
</StaticRouter>
</Provider>
);
const initialState = serialize(store.getState());
const helmet = Helmet.renderStatic();
res.render('index', { content, initialState, helmet });
});
}
module.exports = handleRender;

index.胡子:

<!DOCTYPE html>
<html dir="ltr" lang="en-US">
<head>
{{{ helmet.title }}}
{{{ helmet.meta }}}
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link href="https://fonts.googleapis.com/css?family=Pathway+Gothic+One:300,400,500,600,700|Lato:300,400,400italic,600,700|Raleway:300,400,500,600,700|Crete+Round:400italic" rel="stylesheet" type="text/css" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link rel="stylesheet" href="/dist/css/style.min.css" type="text/css" />
</head>
<body>
<div id="root">{{{ content }}}</div>

<!-- Turbo library imports: jQuery, Turbo CDN, sample app.js -->
<script>
window.INITIAL_STATE = {{{ initialState }}}
</script>
<script type="text/javascript" src="/dist/js/vendor.min.js"></script>
<script type="text/javascript" src="https://cdn.turbo360-dev.com/dist/turbo.min.js"></script>
<script type="text/javascript" src="/dist/bundle/commons.js"></script>
<script type="text/javascript" src="/dist/bundle/app.js"></script> <!-- React code bundle -->
</body>
</html>

客户:

index.js:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { renderRoutes } from 'react-router-config';
import store from './stores';
import routes from './routes';
const initialState = window.INITIAL_STATE;
ReactDOM.hydrate(
<Provider store={store.configure(initialState)}>
<BrowserRouter>
<div>{renderRoutes(routes)}</div>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);

根应用程序组件:

import React from 'react';
import { renderRoutes } from 'react-router-config';
import Header from './partials/Header';
import actions from '../actions';
const App = ({ route }) => {
return (
<div>
<Header />
{renderRoutes(route.routes)}
</div>
);
};
export default {
component: App,
loadData: ({ dispatch }) => dispatch(actions.currentUser())
};

Posts.js:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import swal from 'sweetalert';
import { Link } from 'react-router-dom';
import { CreatePost } from '../view';
import { Account } from '../containers';
import actions from '../../actions';
import { DateUtils } from '../../utils';
class Posts extends Component {
componentDidMount() {
if (this.props.post.all == null) {
this.props
.fetchPosts({})
.then(response => {
return null;
})
.catch(err => {
console.log(err);
});
}
if (this.props.reply.all == null) {
this.props
.getReplies({})
.then(() => {
return null;
})
.catch(err => {
console.log(err);
});
}
}
createPost(params) {
const { currentUser } = this.props.user;
if (currentUser == null) {
swal({
title: 'Oops...',
text: 'Please Login or Register before posting',
type: 'error'
});
return;
}
const updated = Object.assign({}, params, { profile: currentUser });
this.props
.createPost(updated)
.then(data => {
swal({
title: 'Post Created',
text: `Title: ${data.title}`,
type: 'success'
});
})
.catch(err => {
console.log(err);
});
}
render() {
const posts = this.props.post.all;
const { currentUser } = this.props.user;
return (
<div>
<div className="row">
<div className="col-sm-8">
<div className="card-columns">
{posts == null
? null
: posts.map(post => {
return (
<div
key={post.id}
className="card text-white bg-dark mb-3"
style={{ maxWidth: '20rem' }}
>
<div className="card-header">
<Link to={`/post/${post.id}`}>
<img className="card-img-top" src={post.image} alt="Card image cap" />
</Link>
</div>
<div className="card-body text-white">
<h4 className="card-title" style={{ color: 'white' }}>
{post.title.length > 17 ? post.title.substr(0, 17) + '...' : post.title}
</h4>
<p className="card-text">
{post.text.length > 30 ? post.text.substr(0, 30) + '...' : post.text}
</p>
<span>
~{' '}
<Link to={`/profile/${post.profile.id}`} style={{ color: 'white' }}>
<strong>{post.profile.username || 'Anonymous'}</strong>
</Link>
</span>
</div>
<div className="card-footer">
<small className="text-muted">
{DateUtils.relativeTime(post.timestamp)}
</small>
</div>
</div>
);
})}
</div>
</div>
<div className="col-sm-4">
<div className="row">
<div className="col-sm-12">
<Account />
</div>
</div>
{currentUser == null ? null : (
<div className="row" style={{ marginTop: '25px' }}>
<div className="row">
<div className="col-sm-12">
<h3>Create a Post</h3>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<CreatePost onCreate={this.createPost.bind(this)} />
</div>
</div>
</div>
)}
</div>
</div>
</div>
);
}
}
const stateToProps = state => {
return {
post: state.post,
user: state.user,
reply: state.reply
};
};
const dispatchToProps = dispatch => {
return {
createPost: params => dispatch(actions.createPost(params)),
fetchPosts: params => dispatch(actions.fetchPosts(params)),
getReplies: params => dispatch(actions.getReplies(params))
};
};
const loadData = store => {
return store.dispatch(actions.fetchPosts());
};
export default {
loadData: loadData,
component: connect(stateToProps, dispatchToProps)(Posts)
};

客户端/服务器路由:

import React from 'react';
import { Post, Posts, Profile, NotFound } from './components/containers';
import App from './components/App';
export default [
{
...App,
routes: [
{
...Posts,
path: '/',
exact: true
},
{
...Post,
path: '/post/:id'
},
{
...Profile,
path: '/profile/:id'
},
{
...NotFound
}
]
}
];

我的最佳猜测是,在Posts.js中,这个代码:

<div className="col-sm-12">
<Account />
</div>

应该是:

<div className="col-sm-12">
<Account.component />
</div>

或者,您可以在某个更高级别提取component部分。

我基于这样一个事实:Account是从容器导入的,代码的其他部分希望导出的容器的形式为{ component, loadData }

否则,另一个罪魁祸首可能是CreatePost

相关内容

最新更新