React component briefly renders the wrong data - reactjs

I have a Profile component that checks to see if the user passed in url params. If not, the Profile component will render using the current logged in user. If they did, it will pull that value and use that userid for the Profile form.
Everything renders. And the proper profiles eventually appear, however if the user passes in different userid to view, the form will first render their own Profile...THEN render the passed in user.
I'm fairly confident that it's an issue of the props not being set at the time the first render occurs. My question is how can I prevent this?
I'm using react router 4.0. I setup a parent child component trying to do a conditional render...nothing seems to help.
My parent render function looks like this:
{this.props.match.params.userId && (
<ProfileForm
key={this.props.match.params.userId}
profileId={this.props.match.params.userId}
/>
)}
{!this.props.match.params.userId && <ProfileForm />}
My Route statement is this:
<Route exact={true} path={routes.PROFILE} component={Profile} />
<Route path={`${routes.PROFILE}/:userId`} component={Profile} />
Is there a way to prevent it from loading the "non-propped" version first or do I have to create a new component for "others" and have the route go there first?

Related

Issue of re-rendering the component on url change

I'm new to react and I have some trouble re-rendering a component when I do my assignment.
On my navbar I have three links: Home, user and Courses.
If an user clicks on the "courses" (/courses?quick-courses=true#courses), the component will render and display the list of courses(the Links). If the user clicks on one of the Links such as "The Javascript"(/courses/3?title= Javascript) and then click the "Courses" (/courses?quick-courses=true#courses) from the navbar again, the content from the "courses" component will not be render again to display the list of course content.
I tried to use the shouldComponentUpdate() and componentDidUpdate() to assign the this.props.location.search to the state and many other ways to solve this issue. however it doesn't work for me.
Here's my components and what I did so far :components-assignment-3
Many thanks.
when you click /courses?quick-courses=true#courses you render this route <Route path="/courses" component={Courses} />
at this point, that route is loaded and mounted
then you click /courses/3?title= Javascript and you render <Route path={"/courses/:id"} exact component={Course} />
now clicking on /courses?quick-courses=true#courses won't re-render <Route path="/courses" component={Courses} /> again as it's already rendered, you're already inside of this route. that's why none of you lifecycle hooks will fire again and your showCourse will be false so that's why you won't see headerCheck

Hide React Elements Based on Session Storage or Page

I have React application which has a structure similar to the following.
class App extends Component {
render() {
return (
<div className="App">
<NavBar />
<Router>
<Switch>
<Route path="/login" component={LoginPage} />
<Route path="/" exact component={DashboardPage} />
<Route path="/admin" exact component={AdminPage} />
// many other routes
<Route component={NotFound} />
</Switch>
</Router>
</div>
);
}
}
I do not want the login page to display the <NavBar /> element. I tried using the sessionStorage to get the userId and only display the navigation if the value is set. When I do this and go to the login page and the nav bar is not there. But when I log in, it's still not there. If I refresh however, it will appear.
I know one way to solve this is to make some sort of wrapper around the pages that do want the navigation, but I'd rather not have all of that code duplication, etc.
I feel this must be a common want, and I'm missing something dumb. Any help would be appreciated. I'm new to React so I don't follow everything that's going on here. Thanks.
I think your way of conditionally showing the NavBar is the right way. The question is how to trigger a state change so that the render method takes care of hiding and showing the NavBar, when you log in and out. I suggested maintaining a isLoggedIn state in your App component, and rendering the NavBar based on that, instead of directly accessing the SessionStorage. You could then use a custom event to update the state, when SessionStorage changes.
See this question for updating state based on Storage (in short, you fire and handle a custom event for storage changes): How to listen to localstorage in react.js
This might still be more code that you had hoped for, but it's more aligned with how React works, to derive the view (render) from component state.

React Route doesn't pick up the url change

I have something like this:
<Route path="/route/:param" component={DealContainer} />
Then while the component is mounted I am doing a client side redirect:
componentWillMount() {
if (this.props.match.params.param != 'desired_one') {
this.props.history.push('/route/desired_one');
Despite the fact that the url changes the component is not remounted...
Any ideas?
You should resolve this issue by using the Redirect component inside "react-router-dom" package
<Route exact path="/route" component={DealContainer} />
<Route
exact
path="/route/desired"
render={() => <Redirect to="/route/desiredRedirectLocation" />}
/>
<Route path="/route/:param" component={DealContainer} />
This implementation should:
Match the exact base route correctly, without matching any props. Lets say that you want to show a list of items (e.g. /products)
Match the desired item to be redirected (e.g. products/car) and redirect it to the desired location, let's say products/horse
Match any other case that you don't want to redirect /products/:id and correctly take the prop you are after inside the match object.
Explanation
The problem with history.push, is that React will figure out you are using the same component, and will only update the differences, without actually re-mounting the component, which is already mounted.
My example with redirect on the other hand, will intercept the route you want to redirect without mounting the component first, so component will be mounted after the redirect happened, correctly executing the action that you need.

Update property of React component in a separate component

I have a React MaterialUI AppBarcomponent with property title , that I am changing based on the value returned by window.location.pathname. So as the page/url changes, the title will change with it. Looks something like below:
<AppBar
title={this.renderTitle()}
/>
renderTitle() {
if (window.location.pathname === '/home'
return 'home';
} else if (window.location.pathname === '/login'
return 'login';
}
The issue I am running into is that renderTitle() does not get executed if a different component (so not the AppBar) causes the page/url change.
E.g. another separate React component on the page triggers the page to change, which I'd hoped with trigger renderTitle(), but it doesn't... thus the title property never updates. So if I am navigating from /home to /login, the following will happen:
pathname is /home
user presses a button which runs a function, submit(), which is used to change the page w/ react-router
renderTitle() is run at this point, but window.location.pathname is still returning the previous page
submit() changes the page to /login
window.location.pathname is now correctly set to /login, but it is too late as renderTitle() has already been run
any help is appreciated, thanks
The best way is to use react-document-title library.
From documentation:
react-document-title provides a declarative way to specify document.title in a single-page app.
This component can be used on server side as well.
If your component that renders AppBar is an actual route you can just read the pathname from the props that react router injects.
<Router>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="messages/:id" component={Message} />
</Route>
</Route>
</Router>
For example in App, About, Inbox and Message you have access to the router props. And you can also pass the props to their children.
render() {
return (
<AppBar
title={this.renderTitle(this.props.location.pathname)}
/>
);
}
And in your function just use the parameter to return the correct result. Now because you are using props your component will update automatically when they change.

Hide parent component in react-router

In react router is there a way to have child routes but not display the parent component? I'm trying to display breadcrumbs in my application so I switched to having child routes but I don't want to display the parent component in the way of a 'master/detail' type layout. I just want the child component displayed as if it were navigated to by itself but it seems if you don't include {this.props.children} somewhere in the parent component then then child component never gets rendered.
One hack I've tried is seeing if this.props.children is populated and not displaying the rest of the page in render like
if(this.props.children) {
return (this.props.children)
} else {
return /**rest of render method**/
}
but this feels like a bad solution.
I'm having trouble understanding exactly what you're trying to do, but it seems <IndexRoute> could help you, since that lets you distinguish what you do at /portal vs. /portal/feature
<Route path="/portal" component={ComponentContainingBreadcrumbs}>
<IndexRoute component={ComponentWithRestofRenderMethodFromYourExample} />
<Route path="child1" component={Child1} />
<Route path="child2" component={Child2} />
</Route>

Resources