Reputation: 57
I have a component (DatasetPage) which renders some images of different datasets for an image selected. This depends on the tab clicked on the navigation top bar. The thing is that one of the dataset in my case is very big and so it takes more time to load the page. If I wait until the page is loaded everything works well but, if I click into another tab (another dataset) before the reducer delivers the properties to my component (ImageWithRelateds), the new page is loaded with the information of the other(last) dataset, which was not loaded yet.
So, I have thought about a solution which could be block the navigation through the navigation bar while I have the Loading running. But the thing is that this loading thing is controlled in the ImageWithRelateds.js component and the navigation bar is controlled from App.js. So I would need to access from App.js to the isLoading attribute of ImageWithRelateds.js (which I already have) but I don't know how to do it. I just found ways to access from children to parent attributes but not backwards. If you could help me with that or just proposing another solution I would be very grateful.
App.js
import React, { Component } from 'react';
import { IndexLink } from 'react-router';
import '../styles/app.scss';
class App extends Component {
constructor(props){
super(props);
this.options = this.props.route.data;
}
renderContent(){
if(this.props.route.options) {
return(<div className="navbar-header nav">
<a className="navbar-brand" id="title" href="/" >
IMAGES TEST WEB
</a>
<li className="nav-item" key={`key-9999`}>
<IndexLink to='/home' className="nav-link active" href="#">HOME</IndexLink>
</li>
{this.props.route.options.map((opt,i)=>{
return this.returnOptions(opt,i);
})}
</div>
);
}
}
returnOptions(opt,i){
return(<li className="nav-item" key={`key-${i}`}>
<IndexLink to={opt.link} className="nav-link active"
href="#">{opt.name}</IndexLink>
</li>);
}
render() {
return (
<div className="main-app-page">
<nav className="navbar navbar-default color-navbar fixed">
<div className="container-fluid">
{this.renderContent()}
</div>
</nav>
<div className="content">
{this.props.children}
</div>
</div>
);
}
}
export default App;
Routes.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Route, IndexRoute, browserHistory, Link } from 'react-router';
import App from './common/App';
import NotFound from './common/NotFound';
import Store from './store';
import Home from './components/Home';
import DatasetPage from './components/Images/DatasetPage';
import ImageWithRelateds from './components/Images/ImageWithRelateds';
import { options_NavBar } from './customize.js';
import { getQimList, resetQimList} from './actions/index';
const Test = ()=>{
return(<h2 style={{"paddingLeft":"35%"}} >W E L C O M E !</h2>)
};
export default (
<Route path="/" components={App} options={options_NavBar} history={browserHistory}>
<IndexRoute components={Test}/>
<Route path="/home" component={Home} />
<Route path="images" >
<Route path="oxford" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('oxford'));
}} />
<Route path="paris" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('paris'));
}} />
<Route path="instre" component={DatasetPage} onEnter={(e)=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('instre'));
}} />
<Route path=":id" component={ImageWithRelateds} />
</Route>
<Route path="*" component={NotFound} />
</Route>
);
Thank you so much!
Upvotes: 0
Views: 974
Reputation: 625
One of the most basic principle in react that the parent give props to children, and the children emit events to the father (to avoid 2 way binding)
so, your App.js should have state, with isLoading variable, and to the ImageWithRelateds component you should pass an event (function) something like this:
<Route path=":id" render={(props) => <ImageWithRelateds {...props} onFinishLoading={loadingFinished} />}>
and inside your component (that should be with state) should have function like this:
function loadingFinished() {
this.setState(prev => ({ ...prev, isLoading: false }))
}
and then, you would know inside you App.js if the loading inside the ImageWithRelateds component finished, and then you would able to do any validation you would like
I suggest to you to read this article about passing events (functions) to components, why it's needed and how to do it effectively
Hope that helped!
Edit:
your final Routes.js code should look something like that:
export default class Routes extends React.Component {
constructor() {
super();
this.state = { isLoading: false };
this.onLoadingFinishded = this.onLoadingFinishded.bind(this);
}
onLoadingFinishded() {
this.setState(state => {
...state,
isLoading: false
});
}
render() {
return <Route path="/" components={App} options={options_NavBar} history={browserHistory}>
<IndexRoute components={Test}/>
<Route path="/home" component={Home} />
<Route path="images" >
<Route path="oxford" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('oxford'));
}} />
<Route path="paris" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('paris'));
}} />
<Route path="instre" component={DatasetPage} onEnter={(e)=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('instre'));
}} />
<Route path=":id" render={(props) => <ImageWithRelateds
{...props}
onLoadingFinished={this.onLoadingFinishded} />} />
</Route>
<Route path="*" component={NotFound} />
</Route>
}
}
(i can't ensure that code exactly running because i don't have all of your project, but that most likely it)
Upvotes: 1