dannnny
dannnny

Reputation: 169

Rerendering child component when parent state changes in React

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

Answers (1)

Nice Books
Nice Books

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

Related Questions