Home > Mobile >  How to re-render and run useEffect when router change (React js)
How to re-render and run useEffect when router change (React js)

Time:07-16

I have a ChartPage function that gets called three times inside three different routers, inside that ChartPage function I am using "useEffect" that updates some variables according to the chosen route. But for some reason its only triggered once, and when I am going to a different route that using the same ChartPage function the "useEffect" does not get triggerd again and even when the routes are changing the data is still the same from the first route that got triggered.
Can someone help fix that issue, I tried to solve this for hours

basically I want to trigger "useEffect whenever I click one of the buttons"

app.js:

import React from "react";
import Navbar from "./components/Navbar";
import "./App.css";
import Home from "./components/pages/Home";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import ShowStocks from "./components/pages/ShowStocks";
import ChartPage from "./components/pages/ChartPage";

function App() {
  return (
    <>
      <Router>
        <Navbar />
        <Routes>
          <Route path="/" exact element={<Home />} />
          <Route path="/show-stocks" element={<ShowStocks />} />
          <Route
            path="show-stocks/bitcoin"
            element={<ChartPage type={"BTC"} />}
          />
          <Route
            path="show-stocks/ethereum"
            element={<ChartPage type={"ETH"} />}
          />
          <Route
            path="show-stocks/cardano"
            element={<ChartPage type={"ADA"} />}
          />
        </Routes>
      </Router>
    </>
  );
}

export default App;

as you can see I am using ChartPage in three different routes but not with the same data
ChartPage.js:

import React, { Component, useState, useEffect } from "react";
import "../../App.css";
import Chart from "../Chart";
import { Button } from "../Button.js";
import "./ChartPage.css";
import axios from "axios";

function getCoin(type) {
  console.log("requesting");
  var path = `/show-stocks/${type}`;
  return axios.get(path).then((response) => {
    return response.data;
  });
}

export default function ChartPage({ type }) {
  const [values, setValues] = useState(null);
  // type is changing correctly and the function did get called with a different type
  // but useEffect doesn't run on the second time
  // so getCoin does not get triggerd and the data stays the same 
  useEffect(() => {
    getCoin(type)
      .then((response) => {
        setValues(response);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

  return (
    <>
      <div className="chart-page-container">
        <h1>{type} Price Market Value</h1>
        <div className="chart-container">
          {null !== values ? (
            <Chart data={[values, type]} />
          ) : (
            <p>Loading...</p>
          )}
        </div>
        <div className="chart-page-btns">
          <Button
            link="/show-stocks/bitcoin"
            className="btns"
            buttonStyle="btn--outline2"
            buttonSize="btn--large2"
          >
            Bitcoin
          </Button>
          <Button
            link="/show-stocks/ethereum"
            className="btns"
            buttonStyle="btn--outline2"
            buttonSize="btn--large2"
          >
            Ethereum
          </Button>
          <Button
            link="/show-stocks/cardano"
            className="btns"
            buttonStyle="btn--outline2"
            buttonSize="btn--large2"
          >
            Cardano
          </Button>
        </div>
      </div>
    </>
  );
}

as you can see the buttons changing the routes and type do update according to the parameter that the routes sended, but the useEffect does not called again.

CodePudding user response:

Add type to the dependencies array of useEffect:

useEffect(() => {
  getCoin(type)
    .then((response) => {
      setValues(response);
    })
    .catch((error) => {
      console.log(error);
    });
}, [type]); // <= HERE

Keep in mind this code has some issues, as you might end up seeing data for a type that doesn't match the one in the URL if something like this happens:

  • You navigate to /type-a and getCoin('type-a') is called.
  • You navigate to /type-b and getCoin('type-b') is called.
  • type-b finishes loading, so its then callback is executed.
  • type-a finishes loading, so its then callback is executed.
  • You are now in /type-b but see data from type-a.

The proper way to do it would be like this:

useEffect(() => {
  let shouldUpdate = true;

  getCoin(type)
    .then((response) => {
      if (shouldUpdate) setValues(response);
    })
    .catch((error) => {
      console.log(error);
    });

  // This cleanup function will be called if the `useEffect`'s
  // dependencies change, so if you run it twice in a row for
  // different types, when the result of the first one arrives,
  // it will be discarded:
  return () => {
    shouldUpdate = false;
  };
}, [type]);
  • Related