[英]React-Redux/Jest Invariant Violation: Could not find “store”
[英]React-redux: Could not find “store”
我是Web开发的新手,我很难学习React,Redux,Express和单页路由,因此如果我缺少明显的解决方法,我深表歉意。
这是我在服务器控制台中遇到的错误: Invariant Violation: Could not find "store" in either the context or props of "Connect(Navbar)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(Navbar)".
Invariant Violation: Could not find "store" in either the context or props of "Connect(Navbar)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(Navbar)".
我很确定我会将根组件包装在带有存储的<Provider>
。
我已经看到其他人报告了类似的问题。
find . -name 'react.js' ./node_modules/babel-types/lib/react.js ./node_modules/react/dist/react.js ./node_modules/react/react.js
但是,如果我删除了babel类型的react.js,则webpack构建会失败。
在我的快速服务器文件中,我正在使用react-router的Router.match()获得单页应用程序。 如果我改用以下内容,则说明商店没有任何问题,但我的应用程序似乎不再是单页应用程序。 如果我对此有误,那么这似乎就是解决方案。 但是,我担心每次我访问新的URL时GET都会请求所有静态文件,但是我可能对SPA的真正含义感到困惑。
app.get('/*', (req,res) => {
res.sendFile(path.join(__dirname, 'views/index.html'))
})
这是我的代码转储:
// Babel ES6/JSX Compiler
require('babel-register');
var _ = require('underscore');
var https = require('https');
var fs = require('fs');
var async = require('async');
var swig = require('swig');
var React = require('react');
var ReactDOM = require('react-dom/server');
var Router = require('react-router');
var routes = require('./app/routes');
var express = require('express');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var Trip = require('./models/trip');
var config = require('./config');
mongoose.connect(config.database);
mongoose.connection.on('error', function() {
console.info('Error: Could not connect to MongoDB. Did you forget to run `mongod`?');
});
var options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
var app = express();
app.set('port', process.env.PORT || 3000);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
/**
* POST /api/trips
* Adds new trip to database.
*/
app.post('/api/trips', function(req, res, next) {
var destination = req.body.destination;
var startDate = req.body.startDate;
var endDate = req.body.endDate;
var comment = req.body.comment;
var trip = new Trip({
destination: destination,
startDate: startDate,
endDate: endDate,
comment: comment
});
trip.save(function(err) {
if(err) {
return next(err);
}
res.send({message: "Trip to " + destination + " has been added successfully!"});
});
});
/**
* GET /api/trips
* Returns all trips
*/
app.get('/api/trips', function(req, res, next) {
Trip.find()
.exec(function(err, trips) {
if (err) {
return next(err);
}
return res.send(trips);
})
});
/**
* PUT /api/trips/:id
* Updates trip given by id
*/
app.put('/api/trips/:id', function(req, res, next) {
// TODO
});
/**
* GET /api/trips/:id
* Returns trip given by id
*/
app.get('/api/trips/:id', function(req, res, next) {
// TODO
});
/**
* DELETE /api/trips/:id
* Deletes trip given by id
*/
app.delete('/api/trips/:id', function(req, res, next) {
// TODO
});
app.use(function(req, res) {
Router.match({routes: (routes.default)(), location: req.url}, function(err, redirectLocation, renderProps) {
if (err) {
res.status(500).send(err.message)
} else if (redirectLocation) {
res.status(302).redirect(redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
var html = ReactDOM.renderToString(React.createElement(Router.RouterContext, renderProps));
var page = swig.renderFile('views/index.html', { html: html });
res.status(200).send(page);
} else {
res.status(404).send('Page Not Found')
}
});
});
var server = https.createServer(options, app);
server.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Trip Planner</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,900"/>
<link rel="stylesheet" href="/css/main.css"/>
<link rel="stylesheet" href="/css/_datepicker.css"/>
</head>
<body>
<div id="app"></div>
<script src="/js/vendor.js"></script>
<script src="/js/vendor.bundle.js"></script>
<script src="/js/bundle.js"></script>
</body>
</html>
import React from 'react';
import {Router, browserHistory} from 'react-router';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import routes from './routes';
import tripPlanner from './reducers/tripPlanner';
let store = createStore(tripPlanner);
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
{routes}
</Router>
</Provider>,
document.getElementById('app'));
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import navbar from './navbar';
import trips from './trips';
const tripPlanner = combineReducers({
trips,
navbar,
form: formReducer
});
export default tripPlanner;
import React from 'react';
import {Route} from 'react-router';
import App from './components/App';
import Home from './components/Home';
import AddTrip from './components/AddTrip';
export default (
<Route component={App}>
<Route path='/' component={Home} />
<Route path='/add' component={AddTrip} />
</Route>
);
import React from 'react'
import NavbarContainer from '../containers/NavbarContainer'
const App = (props) => (
<div>
<NavbarContainer />
{props.children}
</div>
)
export default App
import { connect } from 'react-redux';
import Navbar from '../components/Navbar';
const mapStateToProps = (state) => {
return {
ajaxAnimationClass: state.ajaxAnimationClass
}
}
const mapDispatchToProps = (dispatch) => {
return {
}
}
const NavbarContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Navbar);
export default NavbarContainer;
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
const Navbar = ({ ajaxAnimationClass }) => (
<nav className='navbar navbar-default navbar-static-top'>
<div className='navbar-header'>
<button type='button' className='navbar-toggle collapsed' data-toggle='collapse' data-target='#navbar'>
<span className='sr-only'>Toggle navigation</span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
</button>
<Link to='/' className='navbar-brand'>
<span ref='triangles' className={'triangles animated ' + ajaxAnimationClass}>
<div className='tri invert'></div>
<div className='tri invert'></div>
<div className='tri'></div>
<div className='tri invert'></div>
<div className='tri invert'></div>
<div className='tri'></div>
<div className='tri invert'></div>
<div className='tri'></div>
<div className='tri invert'></div>
</span>
Trips
</Link>
</div>
<div id='navbar' className='navbar-collapse collapse'>
<ul className='nav navbar-nav'>
<li><Link to='/'>Home</Link></li>
<li><Link to='/add'>Add</Link></li>
</ul>
</div>
</nav>
);
export default Navbar;
{
"name": "trip-planner",
"description": "Trip planner",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js",
"watch": "nodemon server.js"
},
"babel": {
"presets": [
"es2015",
"react"
]
},
"dependencies": {
"async": "^1.5.0",
"babel-loader": "^6.2.10",
"babel-preset-react": "^6.22.0",
"body-parser": "^1.14.1",
"colors": "^1.1.2",
"compression": "^1.6.0",
"css-loader": "^0.26.1",
"express": "^4.13.3",
"history": "^1.13.0",
"moment": "^2.17.1",
"mongoose": "^4.2.5",
"morgan": "^1.6.1",
"react": "^15.4.2",
"react-addons-shallow-compare": "^15.4.2",
"react-datepicker": "^0.40.0",
"react-dates": "^5.2.0",
"react-dom": "^15.4.2",
"react-redux": "^5.0.2",
"react-router": "^2.8.1",
"redux": "^3.6.0",
"redux-form": "^6.5.0",
"request": "^2.65.0",
"serve-favicon": "^2.3.0",
"socket.io": "^1.3.7",
"style-loader": "^0.13.1",
"swig": "^1.4.2",
"underscore": "^1.8.3",
"webpack": "^1.14.0",
"xml2js": "^0.4.15"
},
"devDependencies": {
"babel-core": "^6.1.19",
"babel-preset-es2015": "^6.1.18",
"babel-preset-react": "^6.1.18",
"babel-register": "^6.3.13",
"babelify": "^7.2.0",
"bower": "^1.6.5",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.6.0"
},
"license": "MIT"
}
很抱歉,如果我遗漏了一些明显的内容,并且发布了这么长时间,但是我希望它对了解所有必要信息很有用。 如果您需要其他任何帮助,请告诉我。
TLDR:在您的express server.js中,用于路由:
app.get('/*', (req,res) => {
res.sendFile(path.join(__dirname, 'views/index.html'))
})
如果您的应用不再充当SPA,而您希望它充当SPA,请修复组件声明,使其看起来像class MyComponent extends React.Component {...}
我想我找到了解决方案。 我不得不承认我不知道为什么它确实起作用,但是它确实起作用。
首先,我最初在server.js中错误地路由了我的应用程序,如下所示:
app.use(function(req, res) {
Router.match({routes: (routes.default)(), location: req.url}, function(err, redirectLocation, renderProps) {
if (err) {
res.status(500).send(err.message)
} else if (redirectLocation) {
res.status(302).redirect(redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
var html = ReactDOM.renderToString(React.createElement(Router.RouterContext, renderProps));
var page = swig.renderFile('views/index.html', { html: html });
res.status(200).send(page);
} else {
res.status(404).send('Page Not Found')
}
});
});
我使用的方法基于使用alt.js而不是redux的教程 。 我不一定理解为什么redux要求您像这样进行路由:
app.get('/*', (req,res) => {
res.sendFile(path.join(__dirname, 'views/index.html'))
})
但是这样做确实可以摆脱原始错误。 但是,当我尝试使用此方法时,我意识到我的应用程序不再是单页应用程序,因为它正在为每个新URL发送GET请求。 我找到了解决方案,方法是将我的应用程序简化为不带Redux代码的简单div。 我最终发现我的问题出在Navbar.js中(上面有问题)。 无论出于什么原因,当我将声明从
const Navbar ({ajaxAnimationClass}) => {...}
至
class Navbar extends React.Component {...}
我的应用不再在每次加载新页面时发送GET请求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.