Re-render a React Component - javascript

I have a profile component with URL: '/profile/id'.
Now, suppose I have two users having IDs: id_A and id_B.
I am at URL: '/profile/id_A' and I have a button which sends me to URL: '/profile/id_B' using:
<Link to={{ pathname: `/profile/id_B` }}></Link>
Now, my component won't re-render because there is no state change for which I cannot retrieve my user-data from the server since all my requests are done on componentDidMount.
So, how do I re-render my component for the above scenario?
UPD 1:
This is my Route code:
<Route path="/profile/:userId" component={Profile} />
UPD 2:
I have 3 components nested:
Profile -> FollowList -> Card
I have my link in my card component and this is the code for routing it to 'profile/id_B'.
<Link to={{ pathname: `/profile/${this.props.user.userId}` }}></Link>
Can this be a problem that the below answers are not working?

Ciao, to re-render a component you can use shouldComponentUpdate(nextProps) function. Basically React triggers this function to ask you if component should be updated (re-rendered) and here you can write the logic you need to re-render the component. Take a look at this guide.

It appears you need to use the Route component, placing your "pages" on them with one of the designed methods. You can read more about it here:
https://reactrouter.com/web/api/Route
The recommended way of doing it is using the children of the Route, which is supposed to mount the page when the url matches, and unmount when they don't. So something like:
<Route path="/to">
<My Page/>
</Route>

The thing is that your component is getting rerendered but as you have your code in componentDidMount and it is called only once when the component is mounted so you are facing the difficulty.
So to fix this problem you need to unmount and mount your component again, this can be done using the inline function inside component prop.
According to react-router-docs
When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component.
So you need to refactor your code and use
<Route path="/profile/:userId" component={() => (<Profile />)} />
This will mount and unmount on component on each render and you can access the component did mount.
Alternate Solution
You can also use ComponentDidUpdate and check on each update if the id is same or not, if id is not same fetch the data.
componentDidUpdate(prevProps) {
if (prevProps.match.params.userId !== this.props.match.params.userId) {
//call your fetch function here and then set the data in state
setState({
userId: data,
});
}
}

Related

React Router 4, Route with stateless component

If I want to route to a stateless component I use the component which will give me a match params
e.g.
<Route path="/ingredient/:ingredientID" component={IngredientPage} />
the problem with using the "component" attribute is I can't pass in a class function to be used. I'd like to fetch api data based on the id before I load the component and I want the component to be a function component.
if I use render I can't call a state change because that will re-render (infinite loop).
<Route path="/ingredient/:ingredientID" render={(props) => {
this.loadIngredient(props.match.params.ingredientID)
//this will change state which creates a loop
return <IngredientPage ingredient={this.state.ingredient} />
} />
So far the only way I can figure this out is making the component a state component like it's parent but then I am dealing with two states.
Any suggestions?

onEnter prop in react-router v4

I'm using react-router v4. I find it confusing that when I click on <Link> component, the route is immediately changed. I want to stay at the current route before I fetch the data, not to switch the route and then fetch the data. I find that in react-router v3 there's a onEnter hook, but now in react-router v4, it is deprecated. So how can I fetch data right after I click on <Link> component and before the route is changed?
Example:
it's: at A -> click at <Link> to B -> fetching data for B -> render B
not: at A -> click at <Link> to B -> render Route B -> fetching data for B -> rerender B
In react-router v4, you can make use of render prop to Route to replace the onEnter functionality existing in react-router v3
Suppose you have a route like in react-router v3
<Route path="/" component={Home} onEnter={getData} />
The Equivalent of this in react-router v4 would be
<Route exact path="/" render= {() => {getData(); return <Home data={this.state.data}/>}} />
However the suggested method is to make use of lifecycle methods in the component.
From the Github discussion:
We have no lifecycle hooks that receive props on initial render.
We no longer have a "route lifecycle". Instead, since <Match>
components are real components (not pseudo-components like s
were) they get to participate in React's lifecycle, which means they
can just use componentWillMount.
So one of the suggested solution is:
v4 is all about routes just being components. That means taking advantage of
lifecycle methods.
componentWillMount() { // or componentDidMount
fetch(this.props.match.params.id)
}
componentWillReceiveProps(nextProps) { // or componentDidUpdate
if (nextProps.match.params.id !== this.props.match.params.id) {
fetch(nextProps.match.params.id)
}
}

React Router changing params doesn't fire componentWillRecieveProps

The Problem: Changing the parameters of a <Route /> component does not update the component it is rendering. The route change is shown in the URL bar, but directly rendering {this.props.match.params.id} shows the old :id and not the new one reflected in the URL bar.
Update: I fixed this by moving the <BrowserRouter /> out from the index.js file and into the App.js file. It is no longer the direct child of Provider and is instead the child of the App component. No clue why this makes everything suddenly work.
What I am doing: I have a <Link to="/user/11" /> that goes from user/7 (or any current ID) to a /user/11
The componentWillReceiveProps(newProps) of the component it is rendering is not fired.(This component is connected using react-redux if that helps any. I tried applying withRouter around the connection and that did not help)
If I manually refresh the page in chrome (using CTRL-R or the refresh button) the page shows the new data, rendering the "new" param.
TLDR: Switching from /user/7 to /user/11 does not fire that componentWillRecieveProps function and therefore leaving the component displaying the old state
Question: What am I doing incorrectly here that causes componentWillReceiveProps to not fire.
I am using react-router v4 and the latest create-react-app
This is my CWRP function:
componentWillReceiveProps(newProps) {
console.log("getProps")
this.props.getUser(newProps.match.params.id)
if (newProps.match.params.id == newProps.currentUser.id) {
this.setState({ user: "currentUser" })
} else {
this.setState({ user: "selectedUser" })
}
}
This is the full code of my component: https://gist.github.com/Connorelsea/c5c14e7c54994292bef2852475fc6b43
I was following the solution here and it did not seem to work for me. Component does not remount when route parameters change
You'll need to use React Router Redux

React reload component on dynamic route

<Route path="/user/:username/" component={UserProfile} />
I have a route set up like above and having issue on
<Link to={"/user/" + userName + "/"}>user profile</Link>
When user goes to /user/user1/ to /user/user2/ because it does not reload the component but just update the states.
What is the best way to solve this issue? I need HTTP request in componentDidMount to be executes when username changes.
Routed component doesn't reload when route parameters change. But it will call componentWillReceiveProps() as the component get different props. So call HTTP request inside both componentWillReceiveProps() and componentDidMount as React doesn't call componentWillReceiveProps() with initial props during mounting.

How can I prevent React from unmounting/remounting a component?

I am using react-router and react-redux. I have two routes like this:
<Route path='/edit' component={ EditNew } />
<Route path='/edit/:id' component={ EditDraft } />
where EditNew and EditDraft are data-providing containers that wrap an Editor component using the react-redux connect function:
const EditNew = connect(state => ({}))(React.createClass({
render() {
return <Editor />;
}
}));
and
const EditDraft = connect(state => ({ drafts: state.drafts }))(React.createClass({
render() {
const { params, drafts } = this.props;
const draft = findDraft(params.id, drafts);
return <Editor draft={ draft } />;
}
}));
Now, Editor is rigged up in such a way that when you begin typing into a blank Editor, it triggers a history.replaceState() from /edit to /edit/:id with a ranomly generated ID. When this happens, I get the following sequence of events:
EditorNew unmounts
Editor unmounts
EditorDraft renders and mounts
Editor renders and mounts
When I coded these two containers, I thought that the Editor component contained in both of them would be reconciled without unmounting and remounting. This is problematic for me for several reasons besides the extra unnecessary work, chief among which are that the editor ends up losing focus and proper cursor range after the unmount and remount.
To no avail I have tried specifying key for the Editor component to hint to the reconciliation system that it's the same component, and I've tried shouldComponentUpdate, but that doesn't get called, which makes sense given what React is doing.
Apart from combining the two containers into one container with more complicated render() logic, is there anything I can do to prevent the Editor component from unmounting/remounting during the history transition?
React’s Reconciliation Algorithm says that if the element has a different type (in this case, EditNew and EditDraft), then React will “tear down the old tree and build the new tree from scratch.”
To prevent this, you need to use the same component for both routes.
You can use shouldComponentUpdate and, if the route has changed from /edit to /edit/:id (you can check this getting the router info from the state connected to your component) return false, so it won't refresh the component.
Chances are that this isn't possible with react-router <= v3.
With react-router v4, this should be possible now: https://github.com/ReactTraining/react-router/issues/4578

Categories

Resources