I'm Using react-router v4
How can I effectively measure (via navigation timing performance.mark and performance.measure) how long it takes for a user from the click event on the link until the updated / rendered route he navigated to?
How exactly are you defining your routes?
If you are utilizing <Link/> components, you could register your performance mark in an onClick handler as a side-effect. For example, something like the following:
<Link
to="some/great/component"
onClick={() => performance.mark('initialize_page_change')}
/>
Then, in the component that will render as a result of the route-change, you'll want to make a call to performance.mark again. You'll probably want to make use of React's lifecycle methods for this purpose. I'd recommend using the constructor function or componentWillMount(). In your component that corresponds to the above link:
componentWillMount() {
performance.mark('target_page_mounted');
}
And finally, to measure your marks:
performance.measure('reactRouterPerf',
'initialize_page_change',
'target_page_mounted'
);
Related
I have 2 components/pages in my application: orderPage and customerPage, here is navigation in the main component:
import { Switch, Route} from 'react-router-dom';
<Switch>
<Route exact path='/order/:id' component={() => <OrderPage/>}/>
<Route exact path='/customer/:id' component={CustomerPage}/>
</Switch>
Order page has button/link to open CustomerPage for detailed view of the customer data (customer is attribute of the order). There is button/link on the CustomerPage to close the CustomerPage and navigate back to the OrderPage:
import {Link} from 'react-router-dom'
<button type="button">
<Link to={'/order/'+this.props.orderId}>Close</Link>
</button>
The problem is, that such navigation unmounts and destroys the order component. I can check this behaviour by the constructor code in OrderPage by outputting formCreateTimestamp:
constructor(props) {
super(props);
this.state = {
...
formCreatedTimestamp: new Date(),
};
...
}
Can I control this behavior and prevent this unmount and destroy? And where can I do such control? Maybe in the code/markup that is responsible for the navigation? How? Or maybe I can implement more sophisticated logic in the OrderPage to prevent the unmount?
Indeed, there is ComponentWillUnmount lifecylce API https://reactjs.org/docs/react-component.html#componentwillunmount but documentation does not mention that the implementation of this API can be used for preventing the unmount (e.g. by throwing some exception inside function).
Order has customer and it is quite common use case to navigate from the order to see the customer details and then navigate back, of couse, OrderPage should not be modified/deleted from DOM tree in such a case, but nevertheless this happens. One can suggest the use of modal dialog for presenting the customer data, but modal dialogs are bad suggestion for the web application that are targeted to the mobile use.
How can I prevent the unmount in React Components? provides the solution with unregiterLeaveHooks and other solutions, but they are more about preventing navigation (e.g. from OrderPage to CustomerPage) and not about keeping OrderPage in unmounted stated while navigation to CustomerPage. That question also mentions that the prevention of unmount should be done in flux/store and not in componentWillUnmount, but that question did not elaborate that further. Acutally I am using flux/state indeed and I would be happy to implement unmounting control logic at the business level (Redux store) and not in some specific component functions.
Prevent component be unmounted with React-router already elaborates my situation and its answers say that such React-Router behavior (automatic creation and unmount of components) is by design and that the only solution is to use the always-existing components (which are placed outside Switch statement) that are made visible/unvisible by relevant CSS. So, possibly my question is duplicate.
I have a React application where pages are linked using React router. The user is provided with several links. Each link is handled through the router.
All of the corresponding pages have similar logic before render function, so I used a URL parameter, a single Route path, and the same target component. The URL parameter is supposed to be passed to the backend service.
Since the target component is the same and only distinguishing factor is the URL parameter, once the component is rendered for any of the links, the lifecycle methods like componentWillMount, componentDidMount do not execute again. So, even if I click on another link whatever is the state created by the first hit, same is used for other links. REST call is within componentDidMount. Am I missing something?
<Route path="/location/:locationType" component={MapSelectedLocation}/>
MapSelectedLocation is supposed to handle several locationType and invoke REST service based on that.
The expected result is to execute the same code for different locationType. How can I achieve this?
You need to use componentDidUpdate lifecycle method to do the calculation or each props/state change. Put the check in this method and compare the prevProps and new props value.
Also this method will not get called on initial rendering
Like this:
componentDidMount() {
this.doCalculation();
}
componentDidUpdate(prevProps) {
if(this.props.match.params.locationType != prevProps.match.params.locationType) {
this.doCalculation()
}
}
doCalculation() {
// do the calculation here
}
I'm using React 16.6 and react-router v4 for a master-detail web application. I'm showing headers in a navbar, with individual elements rendered using React Router and Links. Using components I can easily render array elements, but I want to allow in-place editing for each component with state changes propagated back to the parent.
Following details here, I've tried passing a callback function using state in the "to" object. The function isn't available when I inspect the component constructor.
Example
<Link class="col" to={{pathname: `/listing/${id}`, state: {entity: elem, callback: updateComponent}}}>{elem['name']}</Link>
Where updateComponent is a function.
What's the preferred way to approach this scenario?
Just glanced it over and from what I gather the action is for the History.
action - (string) The current action (PUSH, REPLACE, or POP)
You could trigger callbacks with onClick
The use case is that I want to map the root (/) to one of two different components based on whether the user is logged in or not, and I want these two components to reside in different bundles and lazily loaded, so simply putting the login check in the render() method would not do.
I tried to use dynamic route definition with require.ensure() to lazily load the component, and it works for the first time, but after changing the login state the component doesn't get updated (even if I navigate to another route and back to / ).
I tried to force re-rendering the router by setting props on the component that contains the router, both manually and by making it a Redux connected component, and I also tried to add a listener to the Redux store and change the component state in response to login change, but in all of the attempts I got the error "You cannot change ; it will be ignored" and the component doesn't change.
My ugly solution is to have the different component loading code outside of the router, listen to the login state change and in response load the matching component and set it in the wrapping component's state, which is referenced in the render() code. Is there a clean "React-Router-ish" way to do what I want?
React Router 4 pretty much solves this as it made the route configuration part of the component rendering, so having conditional rendering is the same whether it's based on the location or on other props/state.
The closest thing to a clean "React-Router-ish" way to do that is to use the React Router Enterhooks.
An enter hook is a user-defined function that is called when a route is about to be rendered. It receives the next router state as its first argument. The replace function may be used to trigger a transition to a different URL.
So, use the onEnter(nextState, replace, callback?) attribute on your <Route />.
Called when a route is about to be entered. It provides the next router state and a function to redirect to another path. this will be the route instance that triggered the hook.
If callback is listed as a 3rd argument, this hook will run asynchronously, and the transition will block until callback is called.
The general best practice I follow is to place the auth-check flow away from your routes, and place it inside the transition events/hooks.
The usual behavior is - before the route handler actually gets rendered, check the auth, and redirect the user to another route. In your case, if you want to use the same route, but render different components - you should be able to do that using the same technique too. However, that's not a common thing (based on what I've seen), but it should be possible.
For a complete example of this approach, here's the auth-flow code example you can check. It is shared by the creators of React Router, so it looks credible to me.
PS: My answer is valid for React Router versions > 0.13.x.
I'm looking for solution for paging in routing with react-router and redux.
React-router don't fire callback in onEnter hook if only query changes, Router.run method is deprecated, so I'm a bit puzzled. Are there any other thing to do besides manually subscribing on location.change or use of react's lifecycle hooks like willReceiveProps?
Per the comments, the only hook left to you on the <Router> directly is onUpdate. You might also be able to intercept query parameters via a custom RoutingContext, but we don't currently consider that a public API.
We're looking to add a better solution for this use case in the future, but the approaches outlined are the only ones available for the 1.0.0 release.
For anyone using v2.0/v3.0, you can use the route's onChange hook to respond to query changes.
<Route
component={...}
path="..."
onChange={(nextState, replace, callback) => {
// Do something in response to a query change...
}}
/>
onChange(prevState, nextState, replace, callback?)
Called on routes when the location changes, but the route itself
neither enters or leaves. For example, this will be called when a
route's children change, or when the location query changes. It
provides the previous router state, the next router state, and a
function to redirect to another path. this will be the route instance that triggered the hook. If callback is listed as a 4th argument, this hook will run asynchronously, and the transition will block until callback is called.
https://github.com/remix-run/react-router/blob/v3/docs/API.md#onchangeprevstate-nextstate-replace-callback