I'm trying to connect react router v4 with redux but it's not working. I'm using withRouter as described in the docs, but props aren't available in my route components. Any idea?
Here's my code:
index.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import { routerMiddleware } from 'react-router-redux';
import { createLogger } from 'redux-logger';
import thunk from 'redux-thunk';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
const logger = createLogger();
const middleware = [
thunk,
routerMiddleware(history)
];
if (process.env.NODE_ENV === 'development') {
middleware.push(logger);
}
// create store
const store = createStore(
reducers,
applyMiddleware(...middleware)
);
render(
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>,
document.getElementById('root')
);
App.js
import React, { Component } from 'react';
import { Switch, Route, Link, withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
class App extends Component {
componentDidMount() {
this.props.fetchPages(API_PAGES);
this.props.fetchPosts(API_POSTS);
}
render() {
return (
!this.props.pages.isFetching ?
<div>
<ul>
{this.props.pages.data && this.props.pages.data.map(page => (
<li key={page.id}>
<Link to={page.slug}>{page.title.rendered}</Link>
</li>
))}
</ul>
<Switch location={this.props.location}>
<Route path={routes.HOME} exact component={Home} />
<Route path={routes.ABOUT} component={About} />
<Route path={routes.CONTACT} component={Contact} />
<Route path={routes.NEWS} component={News} />
<Route component={NotFound} />
</Switch>
</div> :
<p>Loading...</p>
);
}
}
function mapStateToProps(state) {
return state;
}
export default withRouter(connect(
mapStateToProps,
{ ...actions }
)(App));
reducers/index.js
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
export default combineReducers({
pages,
posts,
routing: routerReducer
});
components/Home.js
import React from 'react';
const Home = (props) => {
console.log('home', props);
return (
<div>
This is the home page.
</div>
);
};
export default Home;
You should pass the props to home component like this to make it available there,
<Route path={routes.HOME} exact component={(props)=><Home {...props} newProps={value you want to pass}/> }/>
Since you are using react-router v4 you need to be using react-router-redux v5. See here
This uses a different npm package to react-router-redux installed with:
npm install --save react-router-redux#next
The routing reducer should then have the router key (not routing) in combineReducers.
You will then need to use ConnectedRouter and pass in your history object like so...
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById('root')
);
Related
I have encountered a problem with th fact that reactDOM.render doesnt render the app despite yarn run compiling sucessfully.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux';
import {PersistGate} from 'redux-persist/integration/react';
import {store ,persistor} from './redux/store.js'
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
Redux configuration in store.js
import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';
import {persistStore} from 'redux-persist';
import createSagaMiddleware from 'redux-saga';
import { fetchGalleryStart } from './gallery/gallery.saga';
import {rootReducer} from './root.reducer.js';
const sagaMiddleware= createSagaMiddleware();
const middlewares = [sagaMiddleware];
if (process.env.NODE_ENV==='development') {
middlewares.push(logger)
}
export const store = createStore(rootReducer, applyMiddleware(...middlewares));
export const persistor = persistStore(store);
sagaMiddleware.run(fetchGalleryStart); //inside run we pass each individual saga
const exportedObject = {
store,
persistor,
};
export default exportedObject;
The app.js
import React from 'react';
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Header from './components/header/header.component.jsx';
import Footer from './components/footer/footer.component.jsx';
import HomePage from './pages/homepage/homepage.component.jsx';
import Contact from './pages/contact/contact.component.jsx';
import About from './pages/about/about.component.jsx';
class App extends React.Component {
render () {
return (
<div className="App">
<BrowserRouter>
<Header />
<div className="wrapper">
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/contact" component={Contact} />
<Route path="/about" component={About} />
<Route path="*">error</Route>
</Switch>
</div>
<Footer/>
</BrowserRouter>
</div>
);
}
};
export default App
When I open the app in the browser, the app is not rendered on the root, page stays blank.
Could you please help me where to look? I am pretty lost how to approach this since i receive no error.
Thank you for your time
Wrap your render()... method with a class component like this:
class App extends React.Component {
render() {
return ();
}
}
export default App;
To declare App correct.
Or get rid of render() method and do a function:
funciton App() => {
return (<div></div>);
}
export default App;
Hope I could help you and have fun!
I can't use connect, with export in App component. No error is thrown, the app starts normally... but all my links in my navbar stop to work (???). The url changes, but the main page is always displayed, like it would break the routing. Here is my code:
import { Route, Switch, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import Layout from './hoc/Layout/Layout';
import BurgerBuilder from './containers/BurgerBuilder/BurgerBuilder';
import Checkout from './containers/Checkout/Checkout';
import Orders from './containers/Orders/Orders';
import Auth from './containers/Auth/Auth';
import * as actions from './store/actions'
class App extends Component {
render () {
return (
<div>
<Layout>
<Switch>
<Route path="/checkout" component={Checkout} />
<Route path="/orders" component={Orders} />
<Route path="/auth" component={Auth} />
<Route path="/" exact component={BurgerBuilder} />
</Switch>
</Layout>
</div>
);
}
}
const mapDispatchToProps = dispatch => {
return {
anything: ()=>dispatch(actions.logout())
}
};
export default connect(null, mapDispatchToProps)(App);
//export default App;
If I comment the 'connect' part, like this:
//export default connect(null, mapDispatchToProps)(App);
export default App;
everything runs ok and all my links are working fine. Why can't I use 'connect' in my App component? I know I'm not dispatching any actions through props, but it should work nontheless, as far as I'm concerned.
Below is my index.js file, which uses th App component:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import burgerBuilderReducer from './store/reducers/burgerBuilder';
import orderReducer from './store/reducers/order';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const rootReducer = combineReducers({
burgerBuilder: burgerBuilderReducer,
order: orderReducer
});
const store = createStore(rootReducer, composeEnhancers(
applyMiddleware(thunk)
));
const app = (
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
);
ReactDOM.render( app, document.getElementById( 'root' ) );
registerServiceWorker();
I think the problem is your mapDispatchToProps:
import {bindActionCreators} from 'redux'
...
const mapDispatchToProps = dispatch => (bindActionCreators({
actionName: () => ({type:'SOME_ACTION', payload: 'SOME_PAYLOAD'}),
doLogout: () => actions.logout(),
}, dispatch))
EDIT: Do this changes to your App.js
<BrowserRouter>
<Switch>
<Route path="/checkout" component={Checkout} />
<Route path="/orders" component={Orders} />
<Route path="/auth" component={Auth} />
<Route path="/" exact component={BurgerBuilder} />
</Switch>
</BrowserRouter>
I think I got it. The solution was to wrap both my App and Layout components (layout holds my navigation items) with "withRouter":
export default withRouter(connect(null, mapDispatchToProps)(App));
export default withRouter(connect(mapStateToProps)(Layout));
trying my hands on React/Redux following the tutorial on Part 3: https://tighten.co/blog/react-101-routing-and-auth.
Part 1 and 2 has been awesome until part 3 where I bumped into the error in the title. I'm pretty sure I've been following the tutorial fine up to this point.
Any help is much appreciated. Thanks in advance SO.
The above error occurred in the <ConnectedRouter> component:
index.js:1446
in ConnectedRouter (at App.js:39)
in App (created by Context.Consumer)
in Connect(App) (at src/index.js:11)
in Provider (at src/index.js:10)
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './containers/App';
import { Provider } from 'react-redux';
import { configureStore } from './store/configureStore';
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
App.js
import React from 'react';
import { ConnectedRouter } from 'react-router-redux';
import { Route, Redirect } from 'react-router-dom'
import { history } from './../store/configureStore';
import { connect } from 'react-redux';
import Header from '../containers/Header';
import Home from '../containers/Home';
import Signup from '../containers/Signup';
import Login from '../containers/Login';
import Favorites from '../containers/Favorites';
const PrivateRoute = ({component: Component, authenticated, ...props}) => {
return (
<Route
{...props}
render={(props) => authenticated === true
? <Component {...props} />
: <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
/>
);
};
const PublicRoute = ({component: Component, authenticated, ...props}) => {
return (
<Route
{...props}
render={(props) => authenticated === false
? <Component {...props} />
: <Redirect to='/favorites' />}
/>
);
};
class App extends React.Component {
render() {
return (
<ConnectedRouter history={history}>
<div>
<Header />
<div className="container">
<Route exact path="/" component={ Home }/>
<PublicRoute authenticated={this.props.authenticated } path="/signup" component={ Signup } />
<PublicRoute authenticated={this.props.authenticated } path="/login" component={ Login } />
<PrivateRoute authenticated={this.props.authenticated } path="/favorites" component={ Favorites } />
</div>
</div>
</ConnectedRouter>
);
}
}
const mapStateToProps = (state) => {
return { authenticated: state.auth.authenticated };
};
export default connect(mapStateToProps)(App);
ConfigureStore.js
import { createStore, compose, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import rootReducer from '../reducers';
import createHistory from 'history/createBrowserHistory';
import { routerMiddleware } from 'react-router-redux';
export const history = createHistory();
export function configureStore(initialState) {
const store = createStore(
rootReducer,
initialState,
compose (
applyMiddleware(ReduxPromise, routerMiddleware(history)),
window.devToolsExtension ? window.devToolsExtension() : f => f
)
//window.devToolsExtension ? window.devToolsExtension() : undefined
);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
The package react-router-redux is deprecated.
Use connected-react-router instead.
Solved it. Reverted to an older but compatible npm package of react-redux, react-router-dom/redux, redux and redux-promise at the time the project was published. Non-issue for now. Not sure what has changed. Will look into it again.
I'm having an issue in where by I switch routes, and the value of a property in my store is reverting back to it's original state. I'm using react 16 with react router v4.
I'm also noticing that the entire App component rerenders when I change routes. That seems over the top. It's running mapStateToProps and mapDispatchToProps every time. I'm noticing the state passed into mapStateToProps is always empty too.
I'm very puzzled.
main file
import React from 'react';
import { render } from 'react-dom';
import { connect } from 'react-redux';
//install routing deps
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import store from './store'
import App from './components/App';
render(
<Provider store={store}>
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
store
import { createStore, compose, applyMiddleware} from 'redux';
import rootReducer from './reducers/index';
const defaultState = { count: 0 };
const enhancers = compose(
window.devToolsExtension ? window.devToolsExtension() : f => f,
);
const store = createStore(rootReducer, defaultState, enhancers);
//adds hot reload for changes to reducer
if(module.hot){
module.hot.accept('./reducers', ()=>{
const nextRootReducer = require(`./reducers/index`).default;
store.replaceReducer(nextRootReducer);
});
};
export default store;
App component
import React, { Component } from 'react';
import { Route, Switch, Redirect, Link } from 'react-router-dom';
import { BrowserRouter, withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actionCreators from '../actions/actionCreators';
class App extends Component {
constructor() {
super();
};
render() {
return (
<div>
main
<a href='/info'>info</a>
<Switch>
<Route exact path="/" render={() => <h1>kawaii world</h1>} />
<Route exact path="/info" render={() => <h1>v kawaii world ;)</h1>} />
</Switch>
</div>
);
};
};
function mapStateToProps(state) {
return {
count: state.count
};
};
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
If you make use of tag a literally you are saying that you are willing to go to another page but in react apps that's not the case. In react you navigate from one component to another.
So to fix your issue you need to do this:
import { BrowserRouter, Link, withRouter } from 'react-router-dom';
class App extends Component {
render() {
return (
<div>
<Link to="/">Main</Link>
<Link to="/info">Info</Link>
<Switch>
<Route exact path="/" render={() => <h1>kawaii world</h1>} />
<Route exact path="/info" render={() => <h1>v kawaii world ;)</h1>} />
</Switch>
</div>
);
};
};
I'm on the latest version of React, React-router, React-router-redux, and react-redux but I can't seem to get any other routes other than my App to render. All I get is a blank page. I'm not doing any nesting, but I suspect it might have to do with my setup... also no errors crop up when examining in console.
my index.js looks like this:
import 'babel-polyfill';
// Write entry-point js for webpack here....
import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux';
import App from './containers/App';
import configureStore from './store/configureStore'
import { Router, Route, browserHistory } from 'react-router';
import { PasswordResetView } from './views/PasswordResetView';
import {Test} from './components/Test';
const store = configureStore()
const reactEntry = document.getElementById('react-root');
// Render....
render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}/>
<Route path="/test" component={Test}/>
</Router>
</Provider>,
reactEntry
)
my App.js is this:
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { loginUser } from '../../actions';
import Navbar from '../../components/Navbar';
import {Link} from 'react-router';
class App extends Component {
render() {
const { dispatch, isAuthenticated, errorMessage, isFetching } = this.props
return(
<div>
<div>
<Navbar
isFetching={isFetching}
isAuthenticated={isAuthenticated}
errorMessage={errorMessage}
dispatch={dispatch}/>
</div>
{this.props.children}
<div>
<Link to="/test">Test</Link>
</div>
</div>
)
}
}
App.propTypes = {
dispatch: PropTypes.func.isRequired,
isFetching: PropTypes.bool.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
errorMessage: PropTypes.string
}
const mapStateToProps = (state) => {
const { isAuthenticated, errorMessage, isFetching } = state.auth
console.log(state.auth)
return {
isAuthenticated,
errorMessage,
isFetching
}
}
export default connect(mapStateToProps)(App)
My Test.js file
import React, {Component} from 'react'
export default class Test extends Component {
render(){
return (
<div>Test</div>
)
}
}
Any help whatsoever would be much appreciated!! I'm sure I've screwed something up somewhere...
I ended up rewriting my App component as more of a home/parent component to house my other routes. I'm guessing I was misusing react-router and had some sort of problem rendering between child and parent routes (although I did use this.props.children to test that.. who knows!)
render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}/>
<Route path="/register(/:token)" component={RegistrationCompleteView}/>
<Route path="/resend-confirmation" component={ResendConfirmationView}/>
<Route path="/password-reset" component={PasswordRequestView}/>
<Route path="/password-reset(/:token)" component={PasswordCompleteView}/>
<Route path="/login" component={LoginView}/>
</Router>
</Provider>,
reactEntry