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>
Related
I have to combine use of Redux and React Router.
I tried react Router alone first and when I was clicking my images I was correctly redirected.
I followed redux tutorial and now when I click my images, I change the address (ex: http://localhost:3000/contact) but nothing displays as if the component was empty.
Root.js
import React from 'react';
import './index.css';
import PropTypes from 'prop-types'
import ReactDOM, { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'
import App from './App'
import Users from './users'
import Book from './Book'
import Notfound from './notfound'
import { Provider } from 'react-redux';
import Store from './redux/Store/store'
import * as serviceWorker from './serviceWorker';
const Root = ({ store }) => (
<Provider store = { Store }>
<Router>
<div>
<Switch>
<Route exact path="/:filter?" component={App} />
<Route path="/users" component={Users} />
<Route path="/book" component={Book} />
<Route path='/manual' component={() => { window.location = 'https://------'; return null;} }/>
<Route path='/contact' component={() => { window.location = 'https://-------'; return null;} }/>
<Route component={Notfound} />
</Switch>
</div>
</Router>
</Provider>
)
Root.propTypes = {
store: PropTypes.object.isRequired
}
serviceWorker.unregister();
export default Root
index.js:
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import myReducer from './redux/Reducers/myReducer'
import Root from './Root'
const store = createStore(myReducer)
render(<Root store={store} />, document.getElementById('root'))
App.js:
import React from 'react'
import { Route, Link, Redirect, withRouter, BrowserRouter as Router } from 'react-router-dom'
import logo from './images/logo.png';
import book from './images/book.png';
class App extends React.Component {
constructor() {
super();
this.state = {
date: new Date()
};
}
render() {
const { date } = this.state;
return (
<div>
<img src={logo} />
<img src={book} onClick={() => this.props.history.push('/book')}/>
<img src={call} onClick={() => this.props.history.push('/contact')}/>
</div>
)
}
}
export default App
Do you know what is wrong ?
A few things I noticed:
When using react router you shouldn't use window.location to redirect since this reloads the whole page. The <Redirect> component from react-router is a better choice here.
Also you shouldn't use the component prop on the <Route>-component for things that aren't actually components, as there's the render prop for that (more on that here).
Furthermore: <Route exact path="/:filter?" component={App} /> is not going to work since :filter? is looking for a variable and exact is looking for an exact match. Moreover you probably shouldn't put the flexible one first since it's going to match every route that you throw at it. So all the following routes are practically unreachable.
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 am trying to navigate through different screens with BottomNavigationAction from material ui and i get this error You should not use <Link> outside a <Router>
Tab.js:
import React from 'react';
import { Link } from 'react-router-dom';
import { withStyles } from '#material-ui/core/styles';
import BottomNavigation from '#material-ui/core/BottomNavigation';
import BottomNavigationAction from '#material-ui/core/BottomNavigationAction';
// icons
import HomeIcon from '#material-ui/icons/Home';
import DashboardIcon from '#material-ui/icons/Dashboard';
import PaymentIcon from '#material-ui/icons/Payment';
import FaceIcon from '#material-ui/icons/Face';
import AtmIcon from '#material-ui/icons/Atm';
const styles = {
root: {
position: 'absolute',
bottom: 0,
width: '100%',
cursor: 'pointer'
},
wrapper: {
minWidth: '0px'
}
};
class Tab extends React.Component {
state = { value: 0 };
handleChange = (event, value) => {
this.setState({ value });
};
render() {
const { classes } = this.props;
const { value } = this.state;
return (
<div>
<BottomNavigation value={value} onChange={this.handleChange} className={classes.root}>
<Link to="/">
<BottomNavigationAction label="Home" value="home" icon={<HomeIcon />} className={classes.wrapper}/>
</Link>
</BottomNavigation>
</div>
);
}
}
export default withStyles(styles)(Tab);
app.js is where i am trying to render the Tab.js so it stays on all the pages! and my routes are also rendered there
App.js
import React, { Component } from 'react';
import {BrowserRouter} from 'react-router-dom';
import Routers from './Routers';
import Tab from './components/Tab';
class App extends Component {
render() {
return (
<div>
<Tab />
<Routers />
</div>
);
}
}
export default App;
routes.js is where i identify routes:
Routes.js
import React from 'react';
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import promise from 'redux-promise';
import { Provider } from 'react-redux';
import Home from './components/Home';
import Profile from './components/Profile';
import Login from './components/auth/Login';
import reducers from './reducers';
import configureStore from './store/configueStore';
import {getAuthToken} from './actions/auth';
const store = configureStore();
const Routers = () => (
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<Route path='/' component={Home} exact={true}/>
<Route path='/login' component={Login} exact={true}/>
<Route path='/register' component={Login} exact={true}/>
<Route path='/profile' component={Profile} exact={true}/>
</Switch>
</div>
</BrowserRouter>
</Provider>
);
export default Routers;
how to i use link in my Tab.js file and make the redirection happen
and i will love an explanation on why this problem is accruing and how will i be able to fix it with my current file structure.
And is my file structure good? as i am having a different file for my routes and rendering it inside my app.js
Your Link and Routes must all have a Router provider higher up in the tree. Also you must use a single BrowserRouter. You can change your configuration to the following
const Routers = () => (
<div>
<Switch>
<Route path='/' component={Home} exact={true}/>
<Route path='/login' component={Login} exact={true}/>
<Route path='/register' component={Login} exact={true}/>
<Route path='/profile' component={Profile} exact={true}/>
</Switch>
</div>
);
class App extends Component {
render() {
return (
<div>
<Provider store={store}>
<BrowserRouter>
<div>
<Tab />
<Routers />
</div>
</BrowserRouter>
</Provider>
</div>
);
}
}
You don't have <BrowserRouter> parent for the link used in your tab.js file. Make following changes to your App.js file to make your code work:
import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom';
import Routers from './Routers';
import Tab from './components/Tab';
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Tab />
<Routers />
</div>
</BrowserRouter />
);
}
}
export default App;
I think you should wrap in .
For details you can also refer to Key Concepts For React-Router and React-Router With Material-UI
I'm using react-router to direct a set of cards on the main page, to other individual pages. However, when I click on a card, the new page renders underneath the set of cards, when what I want is to render ONLY the new page. I think the problem may have to do with that my App.js holds the main page inside it, but I don't know where I should put it, if there should be a separate link to it, etc? I would appreciate any help! Thank you
here is the code for the App.js
import React from 'react';
import Routes from '../containers/routes.js';
import ProjectCards from '../containers/project_cards.js';
export default class App extends React.Component {
render() {
return(
<div>
<ProjectCards />
<Routes />
</div>
);
}
}
here is the main container:
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import ProjectCard from '../components/project_card.js';
import Project1 from '../components/project1.js';
class ProjectCards extends React.Component {
render() {
var projectCards = this.props.projects.map((project, i) => {
return (
<div key={i}>
<Link to={`/${project.title}`}>
<ProjectCard title={project.title} date={project.date} focus={project.focus}/>
</Link>
</div>
);
});
return (
<div>{projectCards}</div>
);
}
}
function mapStateToProps(state) {
return {
projects: state.projects
};
}
export default connect(mapStateToProps)(ProjectCards);
here is the routes container:
import React from 'react';
import Project1 from '../components/project1.js';
import { connect } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
import { withRouter } from 'react-router';
class Routes extends React.Component{
render() {
var createRoutes = this.props.projects.map((project, i) => {
return <Route key={i} exact path={`/${project.title}`} exact component={Project1}/>
});
return (
<Switch>
{createRoutes}
</Switch>
);
}
}
function mapStateToProps(state) {
return {
projects: state.projects
};
}
export default withRouter(connect(mapStateToProps)(Routes));
Set you App file as entry for all components e.g
import React, { Component } from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import Home from '../../ui/components/user/home/Home.jsx';
import Header from './header/Header.jsx';
import Fakebook from '../../ui/components/user/fakebook/Fakebook.jsx';
import Dashboard from '../../ui/components/user/dashboard/Dashboard.jsx';
import NotFound from '../../ui/pages/NotFound.jsx';
export default class App extends Component{
render(){
return (
<BrowserRouter>
<div>
<Header />
<Switch>
<Route exact path="/" component={Fakebook}/>
<Route exact path="/Home" component={Home}/>
<Route exact path="/Dashboard" component={Dashboard} />
<Route exact path="/Dashboard/:userId" component={Dashboard}/>
<Route component={NotFound}/>
</Switch>
</div>
</BrowserRouter>
)
}
}
Now if you studied it you will notice a <Header /> component which is not in a route. I did it that way because my header is constant across my whole app.
This is how I setup my route I make my Route the second file after the index.js file so all my route can be visible.
I'm fairly beginner in react/redux, and I'm creating a simple project using React, Redux and React Router v4. I use Redux to handle the state and it sucessfully passes states to root component Wrapper, but it seems it doesn't pass it to Home or other components.
When I console log this.props.Gallery in Wrapper, the data displays but when I do it on Gallery, it shows 'undefined'. I've passed children in Wrapper using React.cloneElement but it didn't work. Is there an explanation to this and some workaround?
My app project structure is
--wrapper
-home
-gallery
-about
-links
Here are the components
For Routing
App.js
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import Wrapper from './components/Wrapper';
...
//import store
import {Provider} from 'react-redux'
import store , {history} from './store'
//router
const mainRouter=(
<Provider store={store} history={history}>
<Wrapper>
<BrowserRouter>
<Switch>
<Route exact path='/' component={ Home } />
<Route path='/portfolio' component={ Gallery } />
<Route path='/about' component={ About } />
<Route path='/links' component={ Links } />
</Switch>
</BrowserRouter>
</Wrapper>
</Provider>
)
ReactDOM.render(mainRouter, document.getElementById('root'))
Wrapper
import React from 'react'
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
import * as actionCreators from '../actions/actionCreators'
function mapStateToProps(state){
return{
gallery:state.gallery
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(actionCreators,dispatch)
}
class Wrapper extends React.Component{
render(){
return(
<div>
{React.cloneElement(this.props.children, this.props)}
</div>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Wrapper)
Home.js
import React from 'react'
import { NavLink } from 'react-router-dom'
//import component
...
class Home extends React.Component {
render(){
console.log(this.props.gallery)
return(
<div>
<p>this is home page</p>
<Header/>
<Nav/>
<Footer/>
</div>
)
}
}
export default Home;
Update 9/19
I've managed to created nested components in react router v4, but still unable to pass props to its direct children. It throws an error of children being undefined
And i've also updated my component structure to
--Home ~ this is more like a welcome containing app's menu
--Wrapper
-gallery
-about
-links
Here is what I did: I've moved Wrapper to be wrapper of gallery, about and links
App.js
<Provider store={store} history={history}>
<BrowserRouter>
<div>
<Route exact path="/" component={Home}/>
<Route path="/h" component={Wrapper}/>
</div>
</BrowserRouter>
</Provider>
Wrapper.js
function mapStateToProps(state){
return{
gallery:state.gallery
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(actionCreators,dispatch)
}
class HomePage extends React.Component {
render() {
return(
<div>
<Route path={`${this.props.match.path}/portfolio`} component={ Gallery } />
<Route path={`${this.props.match.path}/about`} component={ About } />
<Route path={`${this.props.match.path}/links`} component={ Links } />
</div>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Wrapper)
You are correctly passing the props from Wrapper down to it's children. However, Home and Gallery are not it's children. They are great great grand children. The only child of Wrapper is the element(s) directly within it. In this case, that is BrowserRouter.
There are a great number of ways that you could make these props available to Home and Gallery. But passing directly from Wrapper is not one of them.
The simplest way would be to make a GalleryContainer class. This would be identical to the Wrapper class you already created (include it's connections to redux), except that it would explicitly render Gallery, as such;
render() {
return <Gallery {...this.props}/>;
}
Then use this new GalleryContainer as the component for the /portfolio route.
I finally managed to pass the props to child components.
I created two containers for my project, one as main container and other as parent to sub components Gallery, About and Links.
Here is structure in App.js
const mainRouter=(
<Wrapper>
<Provider store={store} history={history}>
<BrowserRouter>
<div>
<Route exact path="/" component={Home}/>
<Route path="/h" component={Container}/>
</div>
</BrowserRouter>
</Provider>
</Wrapper>
)
Then in Container, I connected it to redux, and created route to its sub children components and used render to render the components and pass gallery props to Gallery
<Route path={`${this.props.match.path}/portfolio`} render={()=><Gallery {...this.props.gallery}/>}/>
Here is the Container
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as actionCreators from '../actions/actionCreators'
import { Route } from 'react-router-dom'
//import components
...
function mapStateToProps(state) {
return {
gallery: state.gallery
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch)
}
class Container extends React.Component {
render() {
return (
<div>
<ResponsiveNav/>
<main>
<Route path={`${this.props.match.path}/portfolio`}
render={ ()=><Gallery{...this.props}/> } />
<Route path={`${this.props.match.path}/links`}
render={ ()=><Link{...this.props}/> } />
<Route path={`${this.props.match.path}/about`}
render={ ()=><About{...this.props}/> } />
</main>
<Footer/>
</div>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Container)