React router, codes after history.push run? - reactjs

Say on your clickHandler
you do
// url change will cause this component to be dismounted and a new component will mount
history.push(url)
// do some more work
dispatch(someMoreWork())
What happens to the code after the push call? is the code guaranteed to run?

history navigation is done only after the sync action within the event queue in javascript are executed.
In your case, javascript will executed all the things within the function where you call history.push and will then navigate to the new Route and it will be a predictable behaviour
You can simply validate the above by executing the dispatch action and then accessing the value in the rendered component.
Check this demo that demonstrates the above behavior by dispatching an action after history.push and accessing the resultant redux state in the rendered route

Related

React router - calling same method outside componentDidMount

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
}

ComponentWillReceiveProps method does not invoke automatically unless there is some API which triggers this through mapStateToProps function

Suppose I have a React class based component named as "ComponentA". Now I have called the action creator (getCategories) inside componentDidMount hook of this component and get its response inside componentWillReceiveProps through mapStateToProps setting up and carrying state inside redux. Now if I switch to "componentB" and trying to get this already setted up state inside componentWillReceiveProps through mapStateToProps of "ComponentB", the componentWillReceiveProps never invoked unless there is an API call which forcing this to invoke. I want to invoke this method of "ComponentB" everytime I switch to this component without API call. Please let me know the correct solution of this problem. Thanks
As per you description, It is hard to resolve your issue, but this is my try.
As you are switching the component, assuming you click some link, you will never get data in componentWillReceiveProps because whenever you are switching the component, that component only go through below process as a newly mounted component.
componentWillMount() -> Render -> componentDidMount().
This is the process for newly mounted component.
The data you want in componentWillReceiveProps will only come when you pass data from parent to child and not when you switch the component.
As you are using redux, and changing some state in one component (ComponentA), you can access the changed state the another component's (ComponentB) ComponentDidMount().

Why is componentWillUnmount firing after next components componentWillMount?

I have a React/Redux application with two components. I need to clear a portion of redux state when the first component unmounts, because the second component will error with the state in that form. I've tried to dispatch an action clearing the chunk of state when the first component unmounts, but the second component begins mounting before the first components componentWillUnmount method is called. When I view dispatched actions in redux-logger, I see the second component dispatching actions from componentWillMount and then componentWillUnmount actions from the previous component called.
This is not the expected behavior is it? I am also using react-router v4. Thanks!
Since React v16, the componentWillUnmount hook can fire asynchronously.
This means that you can't make any assumptions about the order (or timings) of the invocations of these hooks cross-component.

react-router-redux push, state change reset

I have an application that creates a new record and redirect the history to the new record page using react-redux-router push. This redirection is made by a "smart component" inside another smart component.
The state of these components is stored on a redux store.
After redirection if I go back to the previous page it's state is dirty.
Is there a way to reset the state ? Or should I manually clean it up before the redirection.
Currently I'm listening on the reducer for LOCATION_CHANGE and resetting it but this seems manual and hacky.
Shouldn't the component unmount when it's route is not rendered anymore?
I'm unclear from your question - is the state that you're wanting to be reset stored within that component (setState) or within the Redux store?
If it's within that component, and the component is unmounted, then its state should be automatically reset.
If it's within the Redux store, then the whole point of the Redux store is that it exists persistently, outside of whatever components are mounted. One common way of resetting state in that case would be to dispatch a clear or reset action within the component's componentWillMount (i.e., the component ensures that it always starts with a good state).

Is there a clean way to conditionally load and render different components for the same React Router route?

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.

Resources