0

I am trying to build an analytics dashboard that allows user to filter project, toDate, fromDate, timeFrame. And also allow them to clearFilters

enter image description here

Depending upon the range between toDate and fromDate, TimeFrame will be disabled.

useEffect(() => {
    // fetch data only once when any of those dependencies changes
    // or multiple of them changes at the same time.

    // fetchData();
    // setItInTheContext();

    renderCount.current = renderCount.current + 1;
    console.log("API called", `${renderCount.current}`);
  }, [project, fromDate, toDate, timeFrame]);

I want to fetch data only once when any of those dependencies changes or multiple of them changes at the same time.

Issue that I am facing is the delayed response & async behaviour of JS, (refer img.) since grouping data at the backend by Days take more time than weeks, I end up fetching data grouped by days and setting it to the context. Even though days is disabled for that date range.

Following codeSandbox mimics the issue. Please check the sandbox console. CODE_SANDBOX_Link: https://codesandbox.io/s/elated-glade-zbzmyv

I also tried putting all the dependencies in a single filterObject, but the behaviour is the exactly same. https://codesandbox.io/s/gracious-lehmann-4ftsqi

I also tried this approach, but there are quite a few scenarios that need to be taken in consideration. (lots of if's ---probably not a good approach---)

Following files are the same from the codesandbox.(Please ignore if you have already checkout the sandbox link)

// ProjectContext.js

import { createContext, useMemo, useState, useContext } from "react";

const ProjectContext = createContext(null);

export const ProjectProvider = ({ children }) => {
  const [project, setProject] = useState("P1");
  const date = new Date();
  const [fromDate, setFromDate] = useState(
    new Date(date.getFullYear(), date.getMonth(), 1)
  );
  const [toDate, setToDate] = useState(
    new Date(date.getFullYear(), date.getMonth() + 1, 0)
  );
  const [timeFrame, setTimeFrame] = useState("years");

  const value = useMemo(
    () => ({project, setProject, fromDate, setFromDate, toDate, setToDate, timeFrame, setTimeFrame
    }), [ project, setProject, fromDate, setFromDate, toDate, setToDate, timeFrame, setTimeFrame]
  );

  return (
    <ProjectContext.Provider value={value}>{children}</ProjectContext.Provider>
  );
};

export const useProject = () => {
  const context = useContext(ProjectContext);
  if (!context) {
    throw new Error("ProjectContext must be used within ProjectProvider");
  }
  return context;
};

// App.js

import "./styles.css";
import { useEffect, useRef } from "react";
import { useProject } from "./ProjectContext";

export default function App() {
  const { toDate, fromDate, project, timeFrame, setTimeFrame, setFromDate, setToDate, setProject } = useProject();

  const renderCount = useRef(0);

  useEffect(() => {
    // fetch data only once when any of the following dependencies changes
    // or multiple of them changes at the same time.

    // fetchData();
    // setItInTheContext()

    renderCount.current = renderCount.current + 1;
    console.log("API called", `${renderCount.current}`);
  }, [project, fromDate, toDate, timeFrame]);

  useEffect(() => {
    setTimeFrame(`days ${renderCount.current}`);
  }, [toDate, fromDate]);

  const getStringDate = (date) => {
    return new Date(date).toDateString();
  };

  return (
    <div>
      <input
        type="submit"
        onClick={(e) => setProject(`Project ${renderCount.current}`)}
        label="Hello"
        value="Select Project"
      />
      <input
        type="date"
        value={fromDate}
        onChange={(e) => setFromDate(e.target.value)}
        placeholder="Enter from date"
      />
      <input
        type="date"
        value={toDate}
        onChange={(e) => setToDate(e.target.value)}
        placeholder="Enter to date"
      />
      <input
        type="submit"
        value="Change timeFrame"
        onClick={() => {
          setTimeFrame(`weeks ${renderCount.current}`);
        }}
      />
      <input
        type="submit"
        value="Clear Filter"
        onClick={() => {
          setFromDate(new Date());
          setToDate(new Date());
        }}
      />
      <p>Render Count : {renderCount.current}</p>
      <p>Project : {project}</p>
      <p>To Date : {getStringDate(toDate)}</p>
      <p>From Date :{getStringDate(fromDate)}</p>
      <p>TimeFrame : {timeFrame}</p>
    </div>
  );
}

Thank you in advance..!!

Kirill Novikov
  • 2,576
  • 4
  • 20
  • 33
Shreyas Chorge
  • 127
  • 1
  • 6
  • Would debouncing your request help? If you do a search for a React debounce hook you'll see some examples. – Abe Mar 05 '23 at 00:37
  • @Abe No it didn't worked. I tried it in my code, the behaviour is same. I also tried it here https://codesandbox.io/s/gifted-archimedes-u59w5m?file=/src/App.js – Shreyas Chorge Mar 05 '23 at 07:30
  • Went to your CodeSandbox link but didnt understand what you want exactly since no data is being returned in your examples – underflow Mar 05 '23 at 10:04
  • You to *"fetch data only once when any of those dependencies changes"*, so not every time they change right? – underflow Mar 05 '23 at 10:06
  • @underflow Yes "Either any of those dependencies changes or multiple dependencies changes at the same time (i.e clicking on ClearFilter)". – Shreyas Chorge Mar 05 '23 at 10:21
  • If any of them changes a 2nd time, you do not want to fetch data? – underflow Mar 05 '23 at 10:35
  • @underflow For example if I clear filter, The day range will change and that will change the timeFrame in this case I need to fetch the data 2nd time. – Shreyas Chorge Mar 05 '23 at 11:34
  • Cuz in this case timeFrame shifted from days to weeks. So data must be grouped by weeks and get fetched and not by days. And since initial req sent was by days, and grouping data by days takes more time in backend than weeks (since I have a large dataset). The initial response I get is data grouped by weeks and that gets replaced by data grouped by days in the context. – Shreyas Chorge Mar 05 '23 at 11:44
  • Why is this tagged with react-native again? It has nothing to do with React Native. – RubenSmn Mar 05 '23 at 13:19
  • 1
    @RubenSmn react-native devs might have ran into a similar problem, since it is related to state management. – Shreyas Chorge Mar 05 '23 at 13:21

0 Answers0