Reputation: 169
I'm still pretty new to React, so I'm sorry if this has a simple answer. I'm trying to make a bar at the top of the page re-render every time the URL changes (with React Router). After struggling with this for a while (please let me know if you have any thoughts on an easier solution!), I decided to update the state when a URL changes and pass this new state into a component. Here is a simplified version of the code:
import React from 'react';
import {
AppBar, Toolbar, Typography
} from '@material-ui/core';
import './TopBar.css';
class TopBar extends React.Component {
constructor(props) {
super(props);
this.state = {
url: props.url
}
this.parseURL = this.parseURL.bind(this);
}
static getDerivedStateFromProps(props, state) {
if (state.url !== props.url) {
return {
url: props.url
}
}
return null;
}
render() {
return (
<AppBar position="absolute">
<Toolbar>
<Typography variant="h5" color="inherit">
{this.state.url}
</Typography>
</Toolbar>
</AppBar>
);
}
}
export default TopBar;
import React from 'react';
import ReactDOM from 'react-dom';
import {
HashRouter, Route, Switch
} from 'react-router-dom';
import {
Grid, Typography, Paper
} from '@material-ui/core';
import './styles/main.css';
import TopBar from './components/topBar/TopBar';
import UserDetail from './components/userDetail/userDetail';
import UserPhotos from './components/userPhotos/userPhotos';
class PhotoShare extends React.Component {
constructor(props) {
super(props);
this.state = {
url: window.location.href
}
}
render() {
return (
<HashRouter>
<div>
<Grid container spacing={8}>
<Grid item xs={12}>
<TopBar url={this.state.url} key={this.state} />
</Grid>
<Grid item sm={9}>
<Paper className="cs142-main-grid-item">
<Switch>
<Route path="/users/:userId"
render={ props => {
this.state.url = props.match.params.userId;
console.log(this.state.url);
return <UserDetail {...props}
key={props.match.params.userId} />;} }
/>
<Route path="/photos/:userId"
render={ props => {
this.state.url = props.match.params.userId;
console.log(this.state.url);
return <UserPhotos {...props}
key={props.match.params.userId} />;} }
/>
</Switch>
</Paper>
</Grid>
</Grid>
</div>
</HashRouter>
);
}
}
ReactDOM.render(
<PhotoShare />,
document.getElementById('photoshareapp'),
);
The console.log statements when I click a link show that the url state in the PhotoShare component is changing, but the TopBar component is not re-rendering when it changes. Is there any way to make sure it re-renders? Thanks for any help!
Upvotes: 0
Views: 610
Reputation: 1861
Just a few remarks:
Don't use the state as a key for TopBar
, In the render
of PhotoShare
.
React keys should be simple Strings.
You can simply use "TopBar"
as a key.
Anyways it's a single child & a key may not be even required.
Don't use props.url
in your TopBar
constructor (leave it null & let getDerivedStateFromProps
do its work.
I'm wondering if accessing window.location.href
is a side effect. Perhaps, try setState(window.location.href)
inside componentDidMount
instead of initializing it in the constructor itself.
Never set state using this.state.foo =
. The setState
method should be used instead. Not sure about React router, but you cannot update state anyways inside a render
method.
See Detect Route Change with react-router for a better solution.
Upvotes: 1