Permanent States in React - React-Router - reactjs

I have a problem: I have a NavBar that loads an AJAX which retrieves a name and another data. Well, the problem is that this data stores them in states of the declared component itself as NavBar.jsx and when I use routes (react-router) the component is mounted and dismantled losing its states, therefore, every time that change of route has That re-make the AJAX call. This same problem I have for a button 'Enter', which has status 1 and when someone is logged in takes 0 status, but every time I change the route returns to state 1 (as if it were not logged, although it is actually logged). My question is: How can I create states that are maintained despite changing routes? (I know this is not possible, with states I am referring to variables, or anything I can declare that does not change value every time you reassemble the components, in this case NavBar, when changing paths. Thank you.

This can be solved in several ways.
save the returned data in localStorage, check in localStorage before doing the ajax call, is the data exists, just use it instead.
Use redux - this moves the state to the app level instead of the component, unmounting the component will not delete the state.
do not re-render the navbar on each navigation(if it's possible in your app).
See the tutorial for nested routes for react-router ;
https://github.com/reactjs/react-router-tutorial/tree/master/lessons/04-nested-routes
this option means that route changes will only re-render the "internal page" and not constant things like the navbar, submenu, footer etc.

Related

Relatively simple React app has a component re-rendering 7 times on page load

My React app is currently fairly simple in terms of structure, though the logic in the components is getting more intense. The structure is basically Index which has a Header component and, if the user is logged in, will load the Home component, which has components X, Y, and Z on it, corresponding to left sidebar, main area, and right sidebar.
As I'm working on the right sidebar, I have some console.log() statements to help out. I'm now noticing that the right sidebar Z component appears to render 7 times for a given fresh page load, based on how frequently I see my logging. This component has the rough following code:
Declaring various states for the component using React.useState([default value])
Checks the Redux store to fetch the currently authed user of my app (authedUser)
A React.useEffect() that checks if there is an authed user and if so, sets a couple component states. This useEffect triggers off of , [authedUser]); - I suspect this may be a cause of the rerendering?
Some click event handlers, including a more complex one that makes Google API calls when a certain button is clicked, using
The Redux store value for authedUser is used in Index, Header, and Z. The dispatch call that can change this store value only happens within functions relating to the user logging in.
Questions:
Am I right in being concerned that this component is re-rendering so much?
Any ideas on what might be causing it?
Are there any tools I can use to answer the "when/why is this re-rendering?" question?
You have a home component and three children's components on it. You may have a state variable in your home component and pass these state variables as props on to child components. Your home components re-render each time when each state variable changes its state, resulting in child components having to be re-rendered. So as to make the child component re-render only when its related state variable changes its state, you need to use the react memo and the useCallback hook.

Push state without retriggering component load

Lets say I have only one component which fetches some data on being mounted.
I want to be able to change the url (pushState) once the component fetches that data however I dont want to change anything on the page or re-render the component.
Let's say I have a route '/' and it loads a which fetches searched contents on load. Once the data is fetched, I want to be able to update the url to say /searchterm
I am looking to do something similar to
window.history.pushState(null, null, '/searchterm') where the url is updated but nothing on the page changes.
Tried using with state and using this.props.history.push
You could store the route in your state, and then memoize the component with a function that returns true if the route changes, which will prevent an update. If you're still using class components, you can implement similar functionality using shouldComponentUpdate

Resetting a component when leaving a page

I have a page in my react/redux app that has a search field and I can make an ajax request based on input and display the result on the screen. The problem is that whenever I leave the page and return back it preserves the data I fetched. What is a conventional way of resetting a component whenever you leave the page? I should probably do something with componentWillUnmount function, but things don't quite work.
You store the state in a redux store. Thus the behavior - the data is still there in the store when you navigate between pages. It needs to be removed upon navigating away from page. This could be done by dispatching a reset action from the componentWillUnmount lifecycle method of the page component.
Do you need to be storing the fetched data in your redux store? If you are looking for the data to be loaded every time you go to the page and reset whenever you leave, just storing the data in component state would probably be a better fit, since it gives you that behavior by default.

Dynamic React Redux Header component in large application

I'm building a Header component which is used throughout my application. I'm using React and Redux (obvz) to hold a default state for the Header e.g. in the header reducer's default state argument:
state = {
showUserMenu: true,
redirectUrl: '/'
}
This is perfect for all the components where this is true, but for some routes/components I want the header to not show the user menu.
So when those components mount I dispatch an action to HIDE_USER_MENU.
The problem is that because the default is set to true, the userMenu will be there initially, and even if the dispatch is called in componentWillMount, there will be a split second where the userMenu shows.
So don't have a default? But then the reverse is true, it defaults to not showing the menu and only appears once the action has processed.
This is nice, but it didn't go just a step further (for my example) and explain how the reducer would be chosen based on either route or component.
I have also tried firing actions based on location.pathname using react-router-redux, but even this does not happen quickly enough to avoid FOUH (flash of unwanted header! :'( )
I wondered if there was an established pattern for dynamically loading an initial state, that is guaranteed to appear on initial render.
Hope it's clear what I was asking, any help much appreciated!
The simplest approach is to have the Header be a child of the route. This makes it easy to decide what props to pass:
* You don't need to store the header state at all - the routed component can just pass the appropriate props (such as showUserMenu) to the Header.
However, it has a couple of downsides:
* All routes have to take care of rendering the Header (not really a problem, there are lots of ways to share the code)
* When re-routing, the Header is unmounted and a new one mounted (since its parent, the route component, is unmounted). So any DOM and React state will be lost.
The unmount could be avoided by using the same component type for all your routes (which could render the header), but pass it props to configure the appropriate route handling behaviour (such as a child component).
Another (probably bad) option is for your Header state listen for redux-react-router's LOCATION_CHANGE action, and change the value of showUserMenu based on that. The documentation appears to advise against this, since there can be some asynchronicity before the new route is actually rendered due to dynamic route loading, etc., but it probably works fine for the normal case.

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