redux Uncaught Invariant Violation: Could not find "store" in the context - javascript

I am trying to implement redux in this component but I get the following error how could I do this?
the error it shows me is the following:
Uncaught Invariant Violation: Could not find "store" in the context of "Connect(App)". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(App) in connect options.
I know it is possible to do it this way but I don't want to inject the store into the component
store.dispatch(doResetStore());
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { Switch, Route, BrowserRouter } from 'react-router-dom';
import environment from '../../commons/enviroment.const';
import Loader from '../loader/Loader';
import {connect} from "react-redux";
import store from '../../store/store';
import { routes as routesConst, context } from '../../commons/routes/routes.const';
import PropTypes from 'prop-types';
import MandateConsulting from '../mandate-consulting/MandateConsulting';
import { doResetStore } from '../../store/config/actions/actions';
class App extends Component {
componentWillMount(){
this.props.doResetStore();
}
render() {
return (
<Provider store={store}>
<BrowserRouter basename={context()}>
<div id={environment.appName} className="ui-kit-reset">
<Loader />
<Switch>
<Route exact path={routesConst.etd} component={MandateConsulting} />
<Route exact path={routesConst.default} component={MandateConsulting} />
</Switch>
</div>
</BrowserRouter>
</Provider>
);
}
}
App.propTypes = {
reset: PropTypes.any,
doResetStore: PropTypes.any,
};
export const mapStateToProps = state => ({
reset: state.config.reset
});
export const mapDispatchToProps = (dispatch) => ({
doResetStore: () => dispatch(doResetStore()),
});
export default connect(mapStateToProps, mapDispatchToProps)(App);

If you want your App component to be connected to the redux store, you need to wrap App component with <Provider>.
For example - It can be done with the parent component of App i.e. index.js:
index.js
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
App component
...
render() {
return (
<BrowserRouter basename={context()}>
<div id={environment.appName} className="ui-kit-reset">
<Loader />
<Switch>
<Route exact path={routesConst.etd} component={MandateConsulting} />
<Route exact path={routesConst.default} component={MandateConsulting} />
</Switch>
</div>
</BrowserRouter>
);
}
...

Related

Component cannot listen to react-router

I have a component that is used persistently across my spa. I want it to be aware of my router and the various paths that my spa is on. Is there an easy way to do this, or do I have to bandaid some redux (or something similar) state solution that is always listening to my router changes? Thanks! You can see the below for an example.
index.jsx:
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import { Route, Switch } from 'react-router-dom';
import { history, store } from './redux/store';
import Navigation from './navigation';
const UserReport = () => <h2>User Report</h2>;
const UserPage = () => <h2>User Page</h2>;
const Routes = () => (
<React.Fragment>
<Route component={Navigation} />
<Switch>
<Route exact path="/users/:startDate" component={UserReport} />
<Route exact path="/users/:userId" component={UserPage} />
</Switch>
</React.Fragment>
);
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>, document.getElementById('app'),
);
navigation.jsx:
import React from 'react';
import { withRouter } from 'react-router-dom';
const Navigation = (props) => {
console.log(props.match.path);
// expected: "/users/:startDate"
// received: "/"
return (
<h2>Navigation</h2>
);
};
export default withRouter(Navigation);
Since the Navigation route doesn't have any path specified, it always matches whatever path you're on but the match.path only shows you the minimum path required to match for the navigation. That's why it's always /.
You can use location.pathname but it gives you the matched value and not the matched path.
const Navigation = props => {
console.log(props.location.pathname);
// prints `/users/1` if you're on https://blah.com/users/1
// prints `/users/hey` if you're on https://blah.com/users/hey
return <h2>Navigation</h2>;
};
Not sure that's what you want but if you expand what exactly you're trying to achieve, maybe I can help more.
Moreover, your second route to path="/users/:userId" overshadows the first route. Meaning there is no way to tell if hey in /users/hey is startDate or userId. You should introduce a separate route like path="/users/page/:userId".
I ended up using this react-router github discussion as my solution.
An example of my implementation:
index.jsx:
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import { Route, Switch } from 'react-router-dom';
import { history, store } from './redux/store';
import Layout from './layout';
const home = () => <h2>Home Page</h2>;
const users = () => <h2>Users</h2>;
const userPage = () => <h2>User Page</h2>;
const layoutRender = component => route => <Layout component={component} route={route} />;
const Routes = () => (
<Switch>
<Route exact path="/" component={layoutRender(home)} />
<Route exact path="/users" component={layoutRender(users)} />
<Route exact path="/users/:id" component={layoutRender(userPage)} />
</Switch>
);
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>, document.getElementById('app'),
);
layout.jsx:
import React from 'react';
const Layout = (props) => {
const {
component: Component,
route,
} = props;
return (
<div>
<h1>This is the layout</h1>
<Component route={route} />
</div>
);
};
export default Layout;

You should not use Route outside a Router after use of withRouter

I have to use this.props.history.push('/...') in a nested component so I added withRouter() to navigate without history problems using react-router-dom.
But since I have added withRouter, I have You should not use Route outside a Router error.
I have read posts about this error but I can't understand what is wrong with my code.
Root.js:
import { BrowserRouter as Router, Route, Switch, Redirect, withRouter} from 'react-router-dom'
...
const Root = ({ store }) => (
<Router>
<Provider store={store}>
<StripeProvider apiKey="pk_test_XXXXXXXXX">
<Switch>
<Route exact path="/" component={App} />
<Route path="/comp1" component={Comp1} />
<Route path="/comp2" component={Comp2} />
<Route path="/store" component={MyStoreCheckout} />
<Route component={Notfound} />
</Switch>
</StripeProvider>
</Provider>
</Router>
)
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default withRouter(Root)
and index.js:
import { createStore } from 'redux'
import myReducer from './redux/Reducers/myReducer'
import Root from './Root'
import Store from './redux/Store/store'
render(<Root store={Store} />, document.getElementById('root'))
I use withRouter to be able to call this.props.history(...) in CheckoutForm
MyStoreCheckout.js:
class MyStoreCheckout extends React.Component {
render() {
return (
<Elements>
<InjectedCheckoutForm />
</Elements>
);
}
}
export default MyStoreCheckout;
CheckoutForm.js:
class CheckoutForm extends React.Component {
handleSubmit = () => {
fetch(getRequest)
.then((response) => response.json())
.then(...)
.then(() => this.goToSuccessPage())
}
goToSuccessPage(){
this.props.history.push('/') ; //----- error is here if I have no withRouter
render() {
return (
<form onSubmit={this.handleSubmit}>
<DetailsSection/>
<CardSection />
<button>Confirm order</button>
</form>
);
}
}
export default injectStripe(CheckoutForm);
As I mentioned in my comment... Just import withRouter at the top of your CheckoutForm file, then wrap the export with it, at the bottom. Like this:
CheckoutForm.js:
import { withRouter} from 'react-router-dom'
class CheckoutForm extends React.Component {
// ... your class code ...
}
export default withRouter(injectStripe(CheckoutForm));
If your injectStripe HOC doesn't pass all of the props from withRouter down to CheckoutForm, you can try doing export default injectStripe(withRouter(CheckoutForm)); instead, but order shouldn't matter (if set up correctly)

Same component used in multiple routes is being remounted react-router

I created a simple react application. It has a header and three other components called welcome, feature 1 and feature 2.
index.js
import React from 'react'
import { render } from 'react-dom'
import { BrowserRouter, Route } from 'react-router-dom'
import App from './App';
render((
<BrowserRouter>
<Route path="/" component={App} />
</BrowserRouter>
), document.getElementById('root'));
App.js
import React from 'react'
import Header from './Header'
import Main from './Main'
const App = () => (
<div>
<Header />
<Main />
</div>
)
export default App
Header.js
import React, { Component } from 'react';
export default class Header extends Component {
componentDidMount() {
APICall('/user')
}
render() {
return (
<div>I am header</div>
)
}
}
Main.js
import React, { Component } from 'react'
import { Route } from 'react-router-dom'
import Welcome from './Welcome'
import Feature1 from './Feature1'
import Feature2 from './Feature2'
export default class Main extends Component {
render() {
return (
<div>
<Route exact path="/" component={Welcome} />
<Route path="/feature1" component={Feature1} />
<Route path="/feature2" component={Feature2} />
</div>
)
}
}
welcome.js
import React, { Component } from 'react';
export default class Welcome extends Component {
render() {
return (
<div>Welcome!</div>
)
}
}
Feature1.js
import React, { Component } from 'react';
export default class Feature1 extends Component {
render() {
return (
<div>I am Feature1</div>
)
}
}
Feature2.js
import React, { Component } from 'react';
export default class Feature2 extends Component {
render() {
return (
<div>I am Feature2</div>
)
}
}
Welcome, Feature1 and Feature2 are in different routes where as Header is common in all the routes. Say I have a user and I want to show the username on the header. I will make an API call to get the username in componentDidMount() life-cycle hook of header.
Now if I change the route, I don't want the API call to be made again as the username is not going to change. And I thought that is how this was going to behave. As Header component is same in all the routes, I thought Header won't re-mount when I change the route. But that is not what is happening. It is remounting and making the API call again. How can I make sure that the API call is made only once?
I think in this case considering your Header is aware of what User is logged, i.e. App.js state passed down as props, you could use shouldComponentUpdate():
shouldComponentUpdate(nextProps, nextState) {
// see if user changed
if(nextProps.user !== nextState.user){
return true;
}
// returning false will prevent re-rendering
return false;
}
I hope this is somehow useful.
Have a good day!
change to this in index.js:-
<BrowserRouter>
<App />
</BrowserRouter>
It seems you messed up the routing then, because in your router you have only one route registered, which seems to trigger on any child route change. Also, routes should be wrapped in a Switch component.
Check out this structure
/** index.js */
import React from 'react'
import { render } from 'react-dom'
import App from './App';
render(<App />, document.getElementById('root'));
/** App.jsx */
import React from 'react'
import Header from './Header'
import Main from './Main'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
const App = () => (
<div>
<Header />
<BrowserRouter>
<Switch>
<Route exact path="/" component={Homepage} />
<Route exact path="/f1" component={Feature1} />
<Route exact path="/f2" component={Feature2} />
<Route component={NotFound} />
</Switch>
</BrowserRouter>
</div>
)
export default App

React Router Error: You should not use Route outside of Router

I'm just doing some basic routing in my react app and I've done it this way before so I'm pretty confused to as why it isn't working now.
The error I am getting says: You should not use <Route> or withRouter() outside a <Router>
I'm sure this is super basic so thanks for baring with me!
import React from 'react'
import { Route } from 'react-router-dom'
import { Link } from 'react-router-dom'
import * as BooksAPI from './BooksAPI'
import BookList from './BookList'
import './App.css'
class BooksApp extends React.Component {
state = {
books: []
}
componentDidMount() {
this.getBooks()
}
getBooks = () => {
BooksAPI.getAll().then(data => {
this.setState({
books: data
})
})
}
render() {
return (
<div className="App">
<Route exact path="/" render={() => (
<BookList
books={this.state.books}
/>
)}/>
</div>
)
}
}
export default BooksApp
You need to setup context provider for react-router
import { BrowserRouter, Route, Link } from 'react-router-dom';
// ....
render() {
return (
<BrowserRouter>
<div className="App">
<Route exact path="/" render={() => (
<BookList
books={this.state.books}
/>
)}/>
</div>
</BrowserRouter>
)
}
Side note - BrowserRouter should be placed at the top level of your application and have only a single child.
I was facing the exact same issue. Turns out that i didn't wrap the App inside BrowserRouter before using the Route in App.js.
Here is how i fixed in index.js.
import {BrowserRouter} from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>
document.getElementById('root')
);

Access server props from within redux?

I'm trying to use redux, react-engine, and react-router.
The issue or question I have is that react-engine provides an object of props that come from the server. How do I access these props from within my ProvidedApp?
ProvidedApp.js
import React from 'react'
import { connect, Provider } from 'react-redux'
import App from './app'
import { mapStateToProps, mapDispatchToProps, store } from './redux-stuff'
// Connected Component
let ConnectedApp = connect(
mapStateToProps,
mapDispatchToProps
)(App)
let ProvidedApp = () => (
<Provider store={store}>
<ConnectedApp/>
</Provider>
)
export default ProvidedApp
Routes.js
import React from 'react'
import { Router, Route } from 'react-router'
import Layout from './views/Layout'
import App from './views/ProvidedApp'
module.exports = (
<Router>
<Route path='/' component={Layout}>
<Route path='/app' component={App} />
<Route path='/app/dev' component={App} />
</Route>
</Router>
)
I also think my configuration is a little wonky, I couldn't get Provider working any other way. If theres a way to have Provider wrap the Router I'd love to get that working.
Here's some code of what it looks like when I move Provider above Router
ConnectedApp.js
import React from 'react'
import { connect, Provider } from 'react-redux'
import App from './app'
import { mapStateToProps, mapDispatchToProps} from './redux-stuff'
let ConnectedApp = connect(
mapStateToProps,
mapDispatchToProps
)(App)
export default ConnectedApp
Routes.js
import React from 'react'
import { Provider } from 'react-redux'
import { Router, Route } from 'react-router'
import { store } from './redux-stuff'
import Layout from './views/Layout'
import App from './views/ConnectedApp'
module.exports = (
<Provider store={store}>
<Router>
<Route path='/' component={Layout}>
<Route path='/app' component={App} />
</Route>
</Router>
</Provider>
)
I get this error:
Could not find "store" in either the context or props of "Connect(App)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(App)".
Update
I found that I can access from he code in my first example within ProvidedApp. But I have no clue how I'm supposed to pass it into Redux.
let ProvidedApp = (props) => {
console.log(props)
return (
<Provider store={store}>
<ConnectedApp/>
</Provider>
)
}
Seems like I need to wrap the reducer and store and pass in the ServerProps to the default state like this.
let getDefaultState = (serverProps) => {
return {
'appName': serverProps.appName
}
}
let getReducer = (serverProps) => {
let defaultState = getDefaultState(serverProps)
return (state = defaultState, action) => {
}
}
let getStore = (serverProps) => {
let reducer = getReducer(serverProps)
return store = createStore(reducer)
}
let ConnectedApp = connect(
mapStateToProps,
mapDispatchToProps
)(App)
let ProvidedApp = (serverProps) => {
return (
<Provider store={getStore(serverProps)}>
<ConnectedApp/>
</Provider>
)
}
This is super ugly :/

Categories