I've got a sidebar with two buttons, 'test' and 'about'. Test (rocket icon) is rendered at '/test', and About (home icon) is rendered at '/'.
They're both located at the root of the app and are nested within a component.
When I start at '/' and click the Link to="/test" it always loads the 'About' component, and when I check the props for the componentDidMount of 'About', the match object contains match data for "/test".
Only when I refresh does it render the proper component, 'Test', again. Any idea why this is happening?
AppRoutes.js:
export class AppRoutes extends React.Component {
render() {
return (
<div>
<Switch>
<Route
exact path="/"
render={(matchProps) => (
<LazyLoad getComponent={() => import('pages/appPages/About')} {...matchProps} />
)}
/>
<Route
path="/login"
render={(matchProps) => (
<LazyLoad getComponent={() => import('pages/appPages/Login')} {...matchProps} />
)}
/>
<Route
path="/register"
render={(matchProps) => (
<LazyLoad getComponent={() => import('pages/appPages/Register')} {...matchProps} />
)}
/>
<Route
path="/test"
render={(matchProps) => (
<LazyLoad getComponent={() => import('pages/appPages/Test')} {...matchProps} />
)}
/>
...
AboutPage.js && TestPage.js (duplicates except for component name):
import React from 'react';
import SidebarContainer from 'containers/SidebarContainer';
import SidebarPageLayout from 'styles/SidebarPageLayout';
export const About = (props) => {
console.log('About Loading: ', props);
return (
<SidebarPageLayout>
<SidebarContainer />
<div>About</div>
</SidebarPageLayout>
);
}
export default About;
SidebarContainer.js:
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import Sidebar from 'sidebar/Sidebar';
import HamburgerButton from 'sidebar/HamburgerButton';
import AboutButton from 'sidebar/AboutButton';
import ProfileButton from 'sidebar/ProfileButton';
import TestButton from 'sidebar/TestButton';
export class SidebarContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
sidebarIsOpen: false,
sidebarElements: [],
};
}
componentDidMount() {
if (!this.props.authenticated) {
this.setState({
sidebarElements: _.concat(this.state.sidebarElements, HamburgerButton, ProfileButton, AboutButton, TestButton),
});
}
}
toggleSidebarIsOpenState = () => {
this.setState({ sidebarIsOpen: !this.state.sidebarIsOpen });
}
render() {
const { authenticated, sidebarIsOpen, sidebarElements} = this.state;
return (
<div>
<Sidebar
authenticated={authenticated}
sidebarIsOpen={sidebarIsOpen}
sidebarElements={_.isEmpty(sidebarElements) ? undefined: sidebarElements}
toggleSidebarIsOpenState={this.toggleSidebarIsOpenState}
/>
</div>
);
}
}
SidebarContainer.propTypes = {
authenticated: PropTypes.bool,
};
export default SidebarContainer;
Sidebar.js:
import React from 'react';
import _ from 'lodash';
import PropTypes from 'prop-types'
import SidebarStyles from '../styles/SidebarStyles';
export const Sidebar = (props) => {
if (props && props.sidebarElements) {
return (
<SidebarStyles sidebarIsOpen={props.sidebarIsOpen}>
{_.map(props.sidebarElements, (value, index) => {
return React.createElement(
value,
{
key: index,
authenticated: props.authenticated,
sidebarIsOpen: props.sidebarIsOpen,
toggleSidebarIsOpenState: props.toggleSidebarIsOpenState,
},
);
})}
</SidebarStyles>
);
}
return (
<div></div>
);
}
Sidebar.propTypes = {
authenticated: PropTypes.bool,
sidebarIsOpen: PropTypes.bool,
sidebarElements: PropTypes.array,
toggleSidebarIsOpenState: PropTypes.func,
};
export default Sidebar;
TestButton.js:
import React from 'react';
import PropTypes from 'prop-types';
import Icon from 'react-fontawesome';
import {
Link
} from 'react-router-dom';
export const TestButton = (props) => {
return (
<Link to="/test">
<Icon name='rocket' size='2x' />
</Link>
);
}
export default TestButton;
AboutButton.js:
import React from 'react';
import PropTypes from 'prop-types';
import Icon from 'react-fontawesome';
import {
Link
} from 'react-router-dom';
export const AboutButton = (props) => {
return (
<Link to="/">
<Icon name='home' size='2x' />
</Link>
);
}
export default AboutButton;
No refresh, just constant clicking on the '/test' route from the '/' route:
after refresh:
Edit:
Root components:
Edit:
store.js:
import {
createStore,
applyMiddleware,
compose,
} from 'redux';
import createSagaMiddleware from 'redux-saga';
import { rootReducer } from './rootReducers';
import { rootSaga } from './rootSagas';
// sagas
const sagaMiddleware = createSagaMiddleware();
// dev-tools
const composeEnhancers = typeof window === 'object' && (
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? (
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
) : compose
);
export function configureStore() {
const middlewares = [
sagaMiddleware,
];
const store = createStore(
rootReducer,
{},
composeEnhancers(applyMiddleware(...middlewares))
);
sagaMiddleware.run(rootSaga);
return store;
}
export const store = configureStore();
index.js (root):
import React from 'react';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { store } from './store';
import AppContainer from 'containers/AppContainer';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<AppContainer />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
AppContainer:
import React from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { logout, verifyToken } from './actions';
import { selectAuthenticated, selectAuthenticating } from './selectors';
import AppRoutes from 'routes/AppRoutes';
export class AppContainer extends React.Component {
constructor(props) {
super(props);
this.state = { loaded: false };
}
componentDidMount() {
const token = localStorage.getItem('jwt');
if (token) {
this.props.verifyToken(token, () => this.setState({ loaded: true }));
} else {
this.setState({ loaded: true });
}
}
render() {
if (this.state.loaded) {
return (
<AppRoutes
authenticated={this.props.authenticated}
authenticating={this.props.authenticating}
logout={this.props.logout}
/>
);
} else {
return <div>Loading ...</div>
}
}
}
function mapStateToProps(state) {
return {
authenticated: selectAuthenticated(state),
authenticating: selectAuthenticating(state),
};
}
function mapDispatchToProps(dispatch) {
return {
verifyToken: (token = '', callback = false) => dispatch(verifyToken(token, callback)),
logout: () => dispatch(logout()),
};
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(AppContainer));
Edit 2 for LazyLoad:
services/LazyLoad/index.js:
import React from 'react';
export class LazyLoad extends React.Component {
constructor(props) {
super(props);
this.state = {
AsyncModule: null,
};
}
componentDidMount() {
this.props.getComponent() // getComponent={() => import('./someFile.js')}
.then(module => module.default)
.then(AsyncModule => this.setState({AsyncModule}))
}
render() {
const { loader, ...childProps } = this.props;
const { AsyncModule } = this.state;
if (AsyncModule) {
return <AsyncModule {...childProps} />;
}
if (loader) {
const Loader = loader;
return <Loader />;
}
return null;
}
}
export default LazyLoad;