Search code examples
javascriptreactjsreact-hooksstatererender

Trigger a rerender of parent component when a child component is rendered


I am using the following material-ui theme Paperbase and within the Header.js component, I have the following useEffect hook:

const [temperature, setTemperature] = useState([]);

  const getTemperature= async () => {
    try {

      const response = await fetch('/get-temperature')
      const tempData = await response.json();

      setTemperature(tempData);
    } catch (err) {
      console.error(err.message);
    }
  };

useEffect(() => {
    getTemperature();
}, []);  

The main purpose of this, is to display the current temperature as info, within the header component, which gets displayed at first page load/render.

Now within my App.js below, I have the following return setup where the above Header component is called.

return (
    <Router>
      <UserProvider myinfo={myinfo}>
        <Switch>
          <Route path="/">
              <ThemeProvider theme={theme}>
                <div className={classes.root}>
                  <CssBaseline />
                  <nav className={classes.drawer}>
                    <Hidden xsDown implementation="css">
                      <Navigator />
                    </Hidden>
                  </nav>
                  <div className={classes.app}>
                    <Header
                      onDrawerToggle={handleDrawerToggle}
                    />
                    <main className={classes.main}>
                      <Switch>
                        <Route exact path="/new-user"
                          render={(props) => <Content key={props.location.key} />}
                        />
                        <Route exact path="/view-results"
                          render={(props) => <ViewResults key={props.location.key} />}
                        />
                      </Switch>
                    </main>
                  </div>
                </div>
              </ThemeProvider>
          </Route>
        </Switch>
      </UserProvider>
    </Router>
);

My question is, how can I trigger a rerender of Header (parent) whenever the user routes to either /new-user or /view-results which in turn calls either Content.js or ViewResults.js, inorder to make the useEffect in Header.js refresh the data, from the REST api fetch and display the latest temperature in the header again?

Ideally anytime Content.js or ViewResults.js is rendered, ensure that Header.js getTemperature() is called.

Any help would be much appreciated.


Solution

  • Your current code is pretty close to a multi layout system. As being a component child of Route, you can access the current location via useLocation() or even the native window.location.pathname.

    This is my example of multi layout React app. You can try to use it to adapt to your code.

    The MainLayout use a fallback route when no path is specified. It also contains a Header and include a page

    const Dispatcher = () => {
        const history = useHistory();
    
        history.push('/home');
    
        return null;
    };
    
    const App = () => (
        <BrowserRouter>
            <Switch>
                <Route
                    component={Dispatcher}
                    exact
                    path="/"
                />
    
                <Route
                    exact
                    path="/login/:path?"
                >
                    <LoginLayout>
                        <Switch>
                            <Route
                                component={LoginPage}
                                path="/login"
                            />
                        </Switch>
                    </LoginLayout>
                </Route>
    
                <Route>
                    <MainLayout>
                        <Switch>
                            <Route
                                component={HomePage}
                                path="/home"
                            />
                        </Switch>
                    </MainLayout>
                </Route>
            </Switch>
        </BrowserRouter>
    );

    And here is the code for MainLayout

    const MainLayout = ({ children }) => (
        <Container
            disableGutters
            maxWidth={false}
        >
            <Header location={props.location} />
    
            <Container
                component="main"
                maxWidth={false}
                sx={styles.main}
            >
                {children}
            </Container>
    
            <Footer />
        </Container>
    );

    Now that Header can be anything. You need to put a capture in this component

    import { useLocation } from 'react-router-dom'
    
    cont Header = (props) => {
      const { pathname } = useLocation();
      
      //alternatively you can access props.location
      
      useEffect(() => {
        if (pathname === '/new-user') {
          getTemperature();
        }
      }, [pathname]);  
    };

    Note that Header is not a direct descendant of Route therefore it cannot access the location directly via props. You need to transfer in chain

    Route -> MainLayout -> Header
    

    Or better use useLocation