Just a heads up: this is a really strange problem. I'll do my best to clearly explain the issue. This is happening in my ReactJs
app using React Router
.
I have some components that require some type of parameter coming from the URL. I also have components that do NOT depend on a parameter.
If I go to components that do not require any parameters, there are no problems whatsoever.
So, I go to my Home
, then Account List
, neither of which require any parameters, I can go back and forth as many times as I like, there's no problem.
However, if I go to a component that uses a parameter, then try to go another component that uses a parameter, I then get an error. The error indicates that the component react-router
is supposed to load is NOT mounted properly which throws an error telling me that the data the child component needs is missing. The errors I'm getting are pretty simple ones. They simply say something like:
this.props.something is required but undefined
Here's my routes in App.jsx
:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Route, Switch, withRouter } from 'react-router-dom';
// Import components here
// Just showing one for brevity
import Home from '../components/home/Home';
import * as appActions from '../actions/app-actions';
class App extends Component {
constructor(props) {
super(props);
}
render() {
return(
<div>
<Switch>
<Route exact path="/member" component={Home} />
<Route exact path="/member/accounts" component={Accounts} />
<Route exact path="/member/projects" component={ProjectsList} />
<Route path="/member/projects/profile/:id" component={ProjectProfile} />|
<Route exact path="/member/tasks" component={TasksList} />
<Route path="/member/tasks/profile/:id" component={TaskProfile} />
</Switch>
</div>
);
}
}
function mapStateToProps(state) {
return {
member: state.member.memberData
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(appActions, dispatch)
};
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
As you can see from the routes, both ProjectProfile
and TaskProfile
components require ID's. Furthermore, both ProjectProfile
and TaskProfile
components are rather simple parent components that do two things:
- Make an API call to load data in the
componentDidMount()
event - They also load the correct child component based on user's screen resolution
Here's my ProjectProfile
component and the TaskProfile
is pretty much identical to this.
import React, { Component } from 'react'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
// Actions
import * as projectProfileActions from '../../../actions/project-profile-actions';
// Components
import Desktop from './ProjectProfileDesktop';
import Mobile from './ProjectProfileMobile';
class ProjectProfile extends Component {
constructor(props) {
super(props);
};
componentDidMount() {
const id = this.props.match.params.id;
this.props.actions.getData(id);
}
render() {
return (
<div className="height-100 width-100">
<div className="height-100 width-100 row row-clean">
{this.props.ui.isDesktop || this.props.ui.isTablet ? <Desktop />
: this.props.ui.isMobile ? <Mobile />
: null}
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
ui: state.app.window
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(projectProfileActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ProjectProfile);
The strangest part about this is that if I keep going back and forth between ProjectProfile
and any other component and stay away from TaskProfile
, there are no problems. I can even hit ProjectProfile
with different ID's and everything works fine. It all fails only when I go to another component with parameter. I can do the same with TaskProfile
. I can keep hitting TaskProfile
with different ID's OR go back and forth between TaskProfile
and any component without a parameter and everything works fine.
Only if I go to ProjectProfile
first, then try to go to TaskProfile
or vice versa, the error occurs.
The error is simply telling me that the data ProjectProfileDesktop
requires is not there.
Further inspection shows me that after loading a component with a parameter, if I go to another component with a parameter, I'm just NOT hitting the componentDidMount()
method in the second component. Looks like something in the render()
method is causing an issue and preventing it from going to componentDidMount()
. I'm not sure what the issue is because I'm not getting any errors. I put a debugger
at the top of the render()
method. Though I'm not seeing an exact error, the flow shows me that something is definitely going wrong.
Please also notice that I'm using withRouter
. I've seen some issues involving withRouter
but again, I couldn't find anything concrete so far.
I also want to mention that I also have Stripe provider wrapping my App
component. Not sure if this is playing a role in this problem as well. Here's what the render()
looks like in my index.js
:
render(
<Provider store={store}>
<BrowserRouter history={browserHistory}>
<StripeProvider apiKey="my_key">
<App />
</StripeProvider>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
I'd appreciate some pointers on this.
UPDATE:
A behavior I'm observing is that after loading a component with a param, when I hit the second component with a param, I hit the render()
method first and right after the first createElement
, the code jumps to ReactInstrumentation.debugTool.onBeginLifeCycleTimer
-- see screen shot below.
UPDATE 2:
At this point, I'm convinced the issue is not caused by react-router
because I hard-coded the ID I needed into the component so that I don't depend on react-router
or anything else to get it.
I also removed /:id
parameter from the routes for my TaskProfile
and ProjectProfile
components.
I still get the same behavior. So, something is preventing componentDidMount()
method from being called. I also tried componentDidUpdate()
to see if it was being called and it is NOT. I placed componentWillUnmount()
in both components and as I navigate elsewhere in the app, in both components componentWillUnmount()
gets called so one could argue that both components are unmounting OK.
So the bottom line is that something in the render()
method is preventing componentDidMount()
or any other lifecycle method from being called but this is only happening in this particular circumstance.
I'd appreciate any suggestions at this point!!!