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
Related
today i'm trying to implement Redux for the first time on a react-app because it has been a mess to manage state/props, it's pretty good on the redux side so far but when i try to link my store with my app + router i run into errors.
Depending on how i place my router tags 2 things appens:
-not compiling (most of the time because i have outside of the router)
-compiling, rendering but when i try to navigate url changes but not the components that should render.
I did many tries (a lot) so i reverted to when i just linked the store.
Below is my index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import 'bootstrap/dist/css/bootstrap.css';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
And my App.js (shorthen because it's long and messy):
import React, { Component } from 'react';
import { Route, Switch, Link } from 'react-router-dom';
import { connect } from 'react-redux'
// Components imports
import { Provider } from 'react-redux'
import store from './store'
import { ensureAuth, login, register, updateInputAuth, logout } from './actions/authActions'
class App extends Component {
//states
//methods
render() {
const { pathname } = window.location
const { logged, user, loginError, registerError, inputLogin, inputRegister, successMessage } = this.props
return (
<>
<nav className="navbar navbar-expand-lg navbar-light bg-light" id="navbar">
// My app navbar basically, usses <Link> tags
</nav>
{
!logged ?
<>
<ModalConnect />
<ModalRegister />
</>
: null
}
<>
<Switch>
<Route exact path='/' component={Root}/>
<Route path='/ground' render={(props) => <GroundAnalizer {...props} logged={this.state.logged} />} />
<Route path='/air' component={AirAnalizer} />
<Route path='/simulateur' render={(props) => <Simulateur {...props} logged={logged} log={this.connect} reg={this.register} onInputChange={this.onInputChange} register={this.state.register} login= {this.state.login} errors={this.state.errors} errorsLog={this.state.errorsLog} confirmMsg={this.state.confirmMsg} />} />
<Route path='/calculateur-route' component={CalculateurRoute} />
<Route path='/triangulateur' component={Triangulateur} />
</Switch>
</>
</>
)
}
}
export default connect((store) => {
return{
logged: store.auth.logged,
user: store.auth.user,
loginError: store.auth.loginError,
registerError: store.auth.registerError,
inputLogin: store.auth.inputLogin,
inputRegister: store.auth.inputRegister,
successMessage: store.auth.successMessage,
}
})(App)
So there it is, what am i doing wrong and how i should add my store/routing so it does work ?
Take a look at this document.
You need to import withRouter from react-router-dom and wrap the connect export you have there with a call to withRouter in the components that use React Router navigation.
Thus, your code should be something like:
// Before
export default connect((store) => {
return{
logged: store.auth.logged,
user: store.auth.user,
loginError: store.auth.loginError,
registerError: store.auth.registerError,
inputLogin: store.auth.inputLogin,
inputRegister: store.auth.inputRegister,
successMessage: store.auth.successMessage,
}
})(App)
// After
import { withRouter } from 'react-router-dom';
export default withRouter(connect((store) => {
return{
logged: store.auth.logged,
user: store.auth.user,
loginError: store.auth.loginError,
registerError: store.auth.registerError,
inputLogin: store.auth.inputLogin,
inputRegister: store.auth.inputRegister,
successMessage: store.auth.successMessage,
}
})(App))
This link also has some more information on how this works.
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 try to use React Route V4 to build the route of app.
as following:
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
<Provider store= {store}>
<MuiThemeProvider>
<Router history={history}>
<div>
<LayoutContainer/>
<Route exact path="/" component={Home} />
<Route path="/records" render={() =>{
return (<DealsContainer/>)
}}/>
<Route path="/sign_in" component={SignInContainer}/>
</div>
</Router>
</MuiThemeProvider>
</Provider>
I compared the method this.props.history.push('/') and history.push("/"), I find their is different way when I put it to different lifecycle method.
//dealsContainer.js
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import history from '../_helpers/history';
componentDidMount(){
//history.push("/sign_in"); //browser url will change but no redirect
//this.props.history.push("/sign_in"); //it work
}
render(){
if(this.props.authenticate.LoggedIn == false){
//this.props.history.push("/sign_in"); // it work
//history.push("/sign_in"); // it work
}
const mapStateToProps = (state) => {
return{
deals: state.deals,
authenticate: state.authenticate,
}
};
export default withRouter(connect(mapStateToProps)(DealsContainer))
And the history.push("/") in the asyncActions function was not working.
Does it possible to let it work in asyncActions?
I am trying to render a specific component inside of another component based on React, React-Router v4, and Redux in my main 'panel' wrapped in a fixed header and sidebar component.
For example when I select an item from the sidebar, I to render the Detail panel and and load the details based on the id, like: <Route path='/item/:id' component={ItemDetail} />
routes.js
import React, { Component } from 'react';
import { RouteHandler, Switch, Route, DefaultRoute } from 'react-router';
import App from './containers/App';
import Login from './containers/Login';
import LobbyDetail from './components/LobbyDetail';
export default (
<Switch>
<Route exact path="/" component={App} />
<Route exact path="/login" component={Login} />
</Switch>
);
app.js:
import React, { Component } from 'react'
import { Router, Route, Link } from 'react-router'
import { connect } from 'react-redux'
import PropTypes from 'prop-types';
import auth from '../actions/auth';
import Sidebar from '../Components/Sidebar'
class App extends Component {
static propTypes = {
};
/**
*
*/
render() {
const { ... } = this.props
return (
<div className="container-fluid">
<div className="row">
{* I WANT TO RENDER DYNAMIC COMPONENT HERE *}
</div>
<Sidebar currentUser={currentUser}
logout={logout}
/>
</div>
);
}
}
// ...
export default connect(mapStateToProps, mapDispatchToProps)(App)
index.js (basically main app):
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createMemoryHistory } from 'history';
import routes from './routes';
import configureStore from './store/store.js';
import { AppContainer } from 'react-hot-loader';
const syncHistoryWithStore = (store, history) => {
const { routing } = store.getState();
if (routing && routing.location) {
history.replace(routing.location);
}
};
const initialState = {};
const routerHistory = createMemoryHistory();
const store = configureStore(initialState, routerHistory);
syncHistoryWithStore(store, routerHistory);
const rootElement = document.querySelector(document.currentScript.getAttribute('data-container'));
const render = () => {
ReactDOM.render(
<AppContainer>
<Provider store={store}>
<ConnectedRouter history={routerHistory}>
{routes}
</ConnectedRouter>
</Provider>
</AppContainer>,
rootElement
);
}
render();
if (module.hot) { module.hot.accept(render); }
What you're looking for is parameterized routing. Make a <Route/> like the following: <Route path='/item/:id' component={ MyComponent } />.
Now in MyComponent you can use the value of props.match.params.id to conditionally render, or if you're trying to load async data based on the value of :id; You can use the componentWillReceiveProps life cycle method and dispatch an action based on the value of this.props.match.params.id.
Note: <Link to='/item/some-item'/> will set the value of match.params.id to 'some-item'.
It seems my application will not render the component passed to <Route /> unless I refresh the page. What could I be doing wrong?
components/App/index.jsx
// dependencies
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom'
// components
import Header from '../Header';
// containers
import SidebarContainer from '../../containers/SidebarContainer';
import MainContainer from '../../containers/MainContainer';
const App = ({store}) => (
<Provider store={store}>
<Router>
<div className="wrapper">
<Header />
<div className="container-fluid container-fluid--fullscreen">
<div className="row row--fullscreen">
<SidebarContainer />
<MainContainer />
</div>
</div>
</div>
</Router>
</Provider>
);
App.propTypes = {
store: PropTypes.object.isRequired,
};
export default App;
containers/MainContainer.jsx
// dependencies
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Route } from 'react-router-dom'
// components
import Dashboard from '../components/Dashboard';
import List from '../components/List';
// containers
import LoginContainer from './LoginContainer.jsx'
class Main extends Component {
render() {
console.log(this.props)
return(
<div className="wrapper">
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginContainer} />
<Route path="/users" component={List} />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.authentication.token,
};
};
const MainContainer = connect(mapStateToProps, null)(Main);
export default MainContainer;
So it seems when I click on a <Link to="/users" /> component my path changes to http://localhost:3000/users but the component does not change from Dashboard to List
I'm also noticing that when I console.log this.props from MainContainer I do not see anything related to router such as this.props.location.pathname --perhaps I'm not structuring my application correctly?
After poking around the react-router issues page on github I found this thread: https://github.com/ReactTraining/react-router/issues/4671
It appears as though the redux connect method blocks context which is required by react-router package.
That being said, the fix for this is to wrap all redux connected components that have router components inside with withRouter() like so:
containers/MainContainer.jsx
// dependencies
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Route, withRouter } from 'react-router-dom' // IMPORT withRouter
// components
import Dashboard from '../components/Dashboard';
import List from '../components/List';
// containers
import LoginContainer from './LoginContainer.jsx'
class Main extends Component {
render() {
console.log(this.props)
console.log(this.context)
return(
<div className="wrapper">
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginContainer} />
<Route path="/users" component={List} />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.authentication.token,
};
};
// WRAP CONNECT METHOD
const MainContainer = withRouter(connect(mapStateToProps, null)(Main));
export default MainContainer;
I think you have to do little more tweak in your code to make it work. Assuming you use react-router v4, the following should solve your problem.
import { BrowserRouter, Route, Switch } from 'react-router-dom';
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<SidebarContainer />
<MainContainer />
</Switch>
</div>
</BrowserRouter>
</Provider>