Reputation: 1340
I am creating a master-detail navigation just like the example here the react router example;
I have a container component OneCheckpointContainer
, which renders a navigation panel CheckpointNav
(the links), and a data component OneCheckpointData
(this guy fetchs data upon load, and renders OneCheckpointView
.
When I click one of the links in CheckpointNav
nothing happens except the URL in the browser changes. It is not until I refresh the page that new data is fetched, and a new component is then rendered in the children view. (there are also no errors)
I am not sure if this bug is because the child component is also responsible for fetching data, as well as the view.
Here is how I have the routes setup:
<Route path="/modules/:id" component={OneCheckpointContainer}>
<Route path="/modules/:id/checkpoints/:cp_id" component={OneCheckpointData} />
</Route>
OneCheckpointContainer
import React from 'react';
import CheckpointNav from './CheckpointNav';
class OneCheckpointContainer extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
checkpoints: null,
user: null
};
}
componentWillMount() {
this.loadCheckpoints();
this.context.getUser((data) => this.setState({ user: data }));
}
loadCheckpoints() {
$.ajax({
url: `/api/v1/modules/student/${this.props.params.id}/checkpoints`,
method: 'GET',
}).done((data) => {
this.setState({ checkpoints: data });
});
}
render() {
return (
<div>
<div className="col-xs-3">
<CheckpointNav mid={this.props.params.id} checkpoints={this.state.checkpoints} />
</div>
<div className="col-xs-9">
{ this.props.children || <div>No Children Yet</div>}
</div>
</div>
)
}
}
OneCheckpointContainer.displayName = OneCheckpointContainer;
OneCheckpointContainer.contextTypes = {
getUser: React.PropTypes.func.isRequired
};
export default OneCheckpointContainer;
Here is CheckpointNav though I don't believe the bug is in here:
import React from 'react';
import NavLink from '../widgets/NavLink';
const CheckpointNav = (props) => {
const checkpointList = props.checkpoints && props.checkpoints.length > 0 ?
props.checkpoints.map((item) => {
return <li key={item.cp._id} className="list-group-item">
<NavLink className="nav-link" to={"/modules/" + props.mid + "/checkpoints/" + item.cp._id}>
{ item.cp.title}</NavLink></li>
}) : <div>No CPS</div>;
return (
<div className="card one-module-card">
<div className="card-block modules-card-body">
<ul className="list-group tags-group">
{ checkpointList }
<li className="list-group-item new-cp"><NavLink className=""
to={'/post/checkpoint/' + props.mid}
>
New Checkpoint
</NavLink></li>
</ul>
</div>
</div>
);
};
export default CheckpointNav;
OneCheckpointData doesn't fetch new data and render new unless I refresh
import React from 'react';
import CheckpointView from './CheckpointView';
class OneCheckpointData extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
checkpoint: null,
user: null
};
}
componentWillMount() {
this.loadCheckpoint();
this.context.getUser((data) => this.setState({ user: data }));
}
loadCheckpoint() {
$.ajax({
url: `/api/v1/modules/three/cp/${this.props.params.cp_id}`,
method: 'GET',
}).done((data) => {
this.setState({ checkpoint: data });
});
}
render() {
return this.state.checkpoint ? <CheckpointView user={this.state.user} checkpoint={this.state.checkpoint} /> : null;
}
}
OneCheckpointData.displayName = OneCheckpointData;
OneCheckpointData.contextTypes = {
getUser: React.PropTypes.func.isRequired
};
export default OneCheckpointData;
** I have left out OneCheckpointView since it should be irrelavant **
Upvotes: 3
Views: 1787
Reputation: 1913
The problem is that your components are fetching data only before mounting. This only works for the first time they are displayed. These components also need to fetch their data whenever they receive new properties, e.g. ids from the router after navigating to a new location.
In order to do so, the simplest way is to add a componentWillReceiveProps
that compares the previous props with the new one and fetches the new data if required.
An alternative way is to add an onEnter
handler in your Routes configuration to fetch the data whenever a route is changed.
Check this question's answers to see examples of both approaches.
Upvotes: 1
Reputation: 1593
In, that case you can get the props as the parameter of loadCheckpoing(props)
function and you can send the new props on the componentWillReceiveProps
just like below
componentWillMount(this.props) {
this.loadCheckpoint(this.props);
}
componentWillReceiveProps(nextprops) {
this.loadCheckpoint(nextprops);
}
loadCheckpoint(props) {
this.context.getUser((data) => this.setState({ user: data }));
$.ajax({
url: `/api/v1/modules/three/cp/${props.params.cp_id}`, //please check i have replace the this.props to props as we have props as a parameter
method: 'GET',
}).done((data) => {
this.setState({ checkpoint: data });
});
}
i will prefer to use react-redux or reflux library to handle your ajax data query but my proposed solution should do the job for you.
Upvotes: 6
Reputation: 3227
Two steps to diagnosis:
check whether your method loadCheckpoint
has been called every time when you click different link. Just leave one line like console.log(new Date().toLocaleTimeString())
in that method
if step 1 success, then check whether there is a this
reference issue in this part:
(data) => {
this.setState({ checkpoint: data });
}
BTW, you'd better put the ajax call in componentDidMount
method. Refer to: https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount
Upvotes: 1
Reputation: 29
I think this is because you have invoked the function loadcheckpoints in componentwillMount. This is be invoked only once in the component lifecycle. Since the component is already mounted when you had clicked a link in the nav bar, the compoenntwillmount will not be invoked.Hence, no fetch of data occurs.
I guess, you should try the following approach. The onclick function of the links in CheckpointNav should be passed from OneCheckpointContainer . The loadeddata should be set to state in the container which will pass on the data to OneCheckpointData as props .
Upvotes: 1