When answering other question I made incorrect statement writing that ApplicationRoute.beforeModel() hook is ran after every transition. When made aware of this fact I confirmed that the hook is ran only once using barebone Ember app.
Unfortunately, there was nothing I could found in documentation that would explain this behaviour. First paragraph of beforeModel documentation states:
This hook is the first of the route entry validation hooks called when an attempt is made to transition into a route or one of its children.
As for ApplicationRoute - there is not a lot about it, in action bubbling part of the guide we can find information that action bubbles from controller through route to its parent and it lists ApplicationRoute as a top parent of routes:
If neither the template's controller nor the currently active route implements a handler, the action will continue to bubble to any parent routes. Ultimately, if an ApplicationRoute is defined, it will have an opportunity to handle the action.
Logically, this should mean, that every transition should run ApplicationRoute hook which is in contrary to what actually happens.
So the question is:
Why ApplicationRoute doesn't answer to transition events and what are the other differences from defined Routes?
It seems like your main questions is: why doesn't the application route run with every transition? The long answer is a bit complicated, but the short answer is: because it doesn't have to.
For the long answer, let's make an example route hierarchy.
application
index
photos
view
new
A pretty simple set of routes. Now let's suppose that you wanted to visit the photos.view route. Ember would follow these steps:
Run the application route hooks. (Including beforeModel, model, and afterModel).
Run the photos route hooks. (Including beforeModel, model, and afterModel).
Run the view route hooks. (Including beforeModel, model, and afterModel).
Ember has to initialize the route for every parent route of the route you want to visit. That makes sense. But let's say you transitioned from photos.view to photos.new. Ember isn't going to re-run the application and photos route setup hooks. It doesn't need to. Those models have already been resolved and nothing has invalidated them. Ember is only going to run the photos.new setup hooks. If you transitioned to the index route, it would only run setup hooks for that route, not the application route.
Short story long, Ember isn't go to re-run setup hooks and model fetching logic if it doesn't have to. Unless you invalidate some cached data, or force a reload, Ember is only going to run your application route hooks once.
If you want logic that runs before every transition, something that I've done before is to create a base route that all of my routes extend from, then override the activate hook.
Related
So, I wanted to try out Sentry’s withProfiler component to see how it reports component performance. I followed the example at:
https://docs.sentry.io/platforms/javascript/guides/react/#monitor-performance
The only difference is that the component is exported using redux compose(). So with Sentry, it looks something like this:
export default compose(
withStuff,
withMoreStuff,
Sentry.withProfiler,
connect(mapStateToProps)
)(App);
I also tried wrapping a simple counter app as well. Just to see if it'd work without redux.
Then I visited my page, and watched the component render. I expected to see something in the projects performance dashboards. I see a timing for react.mount. But no react.render or react.update, even though the docs indicate it should be traced.
https://docs.sentry.io/platforms/javascript/guides/react/components/profiler/
Anyone know what I might be doing wrong?
Thanks!
Spans data is tracked only when the transaction they belong to finishes, so if there is no ongoing transaction at the time your component updates, nothing about that will be sent to Sentry. When you "visit the page", Sentry's automatic performance tracking starts a pageload or a navigation transaction, and after that ends (which in your case I assume happens quite soon), no performance tracking will be done, unless you programmatically start another transaction (see Custom Instrumentation for React | Sentry Documentation).
My routes look roughly like this:
```
/
/sites
/:site_id
/settings
/user-defined-params
/:param_id
```
Now in /user-defined-params I want to display table-list of parameters assigned to that given site. The models are not related, I mean site doesn't have collection of params as a relation, so I can't simply fetch them via this relation.
Should model() hook for my router return list of these params? By default the model seems to be site loaded from parent route (:site_id). What if loading takes some time and I'd like to actually display this table (so do actual transition) but then show kind of loading indicator waiting for table to fill in with the data.
When I try to load this in model() hook, transition blocks. When I try to load it in afterModel() hook, I don't have a way to assign it and make it available for template (other than force-assigning params property to site model, which seems to be wrong).
All the examples I've found over the Internet seem to be lacking this scenario, which I feel is one of the basic ones, so any hints on that? How should I load it (ideally without blocking transition)?
The defaults in Ember's current router (as you have found) are to block UI loading completely until the promised returned in model hooks have resolved. However, there's interest in adjusting that so we can easily build non-blocking UI patterns with the built-in router.
Right now, the community consensus here is to use ember-concurrency and it's derived state (specifically the .isRunning aspect of tasks) to toggle between a loading state and a screen with the data rendered. It isn't a coincidence that ember-concurrency was built by the guy behind the first router and the current router re-think that is in-process.
One example of an approach to this is outlined here:
https://emberway.io/skeleton-screen-loading-in-ember-js-2f7ac2384d63
I personally prefer to use my route to do the initial task loading and the consume the data in the template. But in a more complex UI I also will go with container components as outlined in that article.
Basically, I would like to fetch the latest data every time user navigates to different tab. For example, I have summary page and details page for customers, which both includes the same customers container component. I'm doing my initial fetching in componentDidMount function, which doesn't get triggered in route change, as component stays in the DOM. Component necessarily doesn't receive new props either.
One solution would be to customize router link with fetch action being dispatched based on the href, but I'm pretty sure this is an anti-pattern, as it complicates code a lot, is hard to maintain, mixes concerns, and may result in a double fetch if target route didn't contain the component already.
Does React provide any lifecycle hook or mechanism that allow me to fetch in response to navigation event alone?
I'm using React-router-component and Redux.
As far as I understand both tabs happen to contain the same component and so React re-uses the DOM and doesn't create a new component instance, right?
You can use React's special key prop to force React to replace a specific instance.
E.g.:
<div className="tab-content>
<div key="tab-1" className="shared-component-across-tabs">
shared-component-across-tabs
</div>
</div>
And in the second tab you'd use key="tab-2".
Otherwise your route handler component will receive props.location.pathname from the router. You can also implement componentWillReceiveProps and check if the pathname has changed and re-fetch the data in that case.
I currently have my application structured so that I have a FormWrapper component which loads the configuration for the passed in FormName, which might be rendered by React-Router like this -
<Route path="/Diary/Test" components={require('./components/wrapperComponents/formWrapper.jsx')} FormName="AppointmentBooking"/>
When FormWrapper loads it calls an ActionCreator and gets the configuration from the server about which page and renders the page passing in the configuration via objProps.
render: function() {
return(React.createElement(PageComponents[this.props.FormName], objProps);
}
The reason for doing things this way is that originally we wanted to be able to allow users to easily be able to customize how forms looked and which components were shown so it allowed us to store the forms and their accompanying child components in configuration and then just load them out with a generic wrapper component. So anything generic to every form could sit in the form wrapper, and then form specific code could sit in each page component and load only the parts relevent to that particular page.
The issue I'm having is that if the AppointmentBooking form also needs to fire an action in the componentWillMount/componentDidMount methods to load some data(for example the options in a dropdown list for appointment locations) I receive a "Cannot dispatch in the middle of a dispatch" error.
My reasoning for the issue was that the initial action triggered by the form wrapper was causing a re-render of the formWrapper because it then had data, and then React.createElement was trying to render the AppointmentBooking component which was also triggering an action thereby causing the dispatcher error.
I've read things about using waitFor() to wait for the first action to complete before triggering the second, but this would then involve the child component knowing about and being dependant on it's parent which I thought might mean I'm not keeping a proper separation of concerns.
Is this a common issue and am I approaching this the right way? And if not is there a suggested way of going about acheiving this?
Flux best practices recommend using "Container" components, and I think doing so may solve your problem. https://facebook.github.io/flux/docs/flux-utils.html#best-practices
A "Container" component is a dummy component whose only purpose is to facilitate store communication. By offshoring that responsibility to a single component, you get around weird race issues like this.
The basic rules for containers are that they should always render the same child components, while passing in unique information (as props) gathered from any relevant store. No conditional logic. Just routing of information.
If you're able to restructure into this paradigm, it'll solve your issues. But that's no small task if you already have actions and listeners spread across your application.
Is there is way to route ALL the actions hitting from a component to the parent controller?
The source of these actions hitting the component is from the sub-components it contains.
Problem here is that i do not readily know from the name of the action coming into the component, but they will be defined in the parent controller.
Depending on the details of your scenario, it's difficult to help, a twiddle might clear things up.
Just make sure you have the following section of the guides down,
calling-actions-up-multiple-component-layers.
What I can recommend without knowing much else, is that if your struggling with handling actions in multiple components, inject a service into the relevant components and handle the state and actions in the service.