簡體   English   中英

React-redux:找不到“商店”

[英]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>

我已經看到其他人報告了類似的問題。

  • 一種情況下 ,用戶沒有連接他的組件,但是我想我正在連接我的組件。
  • 另一種情況下 ,給出了針對React 0.13的解決方法。 但是,我正在使用React 15,並且我也嘗試了解決方法,但仍然沒有運氣。
  • 我最終進入redux的疑難解答部分 不幸的是,我使用的是webpack,無法使用其源映射,因此無法確認我沒有重復的React文件正在運行。 我確實在節點模塊中搜索了react.js文件:

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'))
})

這是我的代碼轉儲:

server.js

// 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'));
});

意見/ index.html的

<!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>

main.js

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'));

減速器/ tripPlanner.js

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;

routes.js

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>
);

組件/ App.js

import React from 'react'
import NavbarContainer from '../containers/NavbarContainer'

const App = (props) => (
  <div>
    <NavbarContainer />
    {props.children}
  </div>
)

export default App

集裝箱/ NavbarContainer.js

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;

組件/ Navbar.js

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;

的package.json

{
  "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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM