Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

130
Views
What is the safe way and best-practice to set a state in parent hierarchy in React to stay DRY?

Consider this scenario:

An admin panel that has a sidebar and a content.

Sidebar contains menu and each item that user clicks, a component would be shown in the content area.

The content is a Router that changes the component based on URL.

Let's say we have these components:

Page1.jsx
Page2.jsx
Page3.jsx

Each of the does some operation when gets loaded. Each does it inside useEffect.

useEffect(() => {
    // some operation inside PageN.jsx that takes some time
    // Here we should show a progress
}, [])

We can easily define a state in each PageN.jsx and based on that return a progress or the actual component:

const [progress, setProgress] = useState()

return progress
   ?
   <div>Loading...</div>
   :
   <div>Actual component</div>

This however is inefficient for an application with 100 pages and kills DRY and also introduces a lot of inconsistencies. Each page might show a different loading component, etc.

One way is to lift-up the progress state to the parent element Router or event the App itself. Then pass it down the hierarchy.

const PageN = ({ setContentProgress }) => {

    // now I can call parent's setContentProgress

But it soon gets into the famous Cannot update a component from inside the function body of a different component.

So, what is the safe approach here? What is the best practice?

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

I would definitely suggest you to use context API, to simply build some layout context on top of your sidebar and content area. Recently I implemented something similar for my personal project where I decided that I want to implement linear progress bar like YouTube has (red line progressing below the header), so the only option for me was to create layout context which will handle progress and then in its children I would simply use exposed actions from context itself, in order to update progress.

I did it in this way:

interface IProps {
  children: React.ReactNode;
}

const initialState = {
  displayProgressBar: false,
  progressPercentage: 0
};

const PROGRES_BAR_TRANSITION = 300; // ms
let timeout: undefined | NodeJS.Timeout;

const Layout = ({ children }: IProps) => {
  const { pathname } = useLocation();
  const [context, setContext] = useState(initialState);
  const navigate = useNavigate();

  useEffect(() => {
    setContext(initialState);

    return () => {
      if (timeout) clearTimeout(timeout);
    };
  }, [pathname]);

  const setLayoutProgressVisiblity = useCallback((isVisible: boolean) => {
    if (!isVisible)
      timeout = setTimeout(() => {
        setContext((p) => ({ ...p, displayProgressBar: isVisible }));
      }, PROGRES_BAR_TRANSITION);
    else setContext((p) => ({ ...p, displayProgressBar: isVisible }));
  }, []);

  const setLayoutProgressPercentage = useCallback(
    (percentage: number) =>
      setContext((p) => ({ ...p, progressPercentage: percentage })),
    []
  );

  const contextActions =
    useMemo<ILayoutContext>(() => ({ setLayoutProgressPercentage, setLayoutProgressVisiblity }),
    [setLayoutProgressPercentage, setLayoutProgressVisiblity]);

  const goToHomepage = useCallback(() => {
    navigate("/", { replace: true });
  }, [navigate]);

  return (
    <LayoutContext.Provider value={contextActions}>
      <div className="app-container p-relative overflow-auto">
        <div>{/** HERE YOU CAN PUT HEADER */}</div>
        {context.displayProgressBar && (
          <HeaderProgressBar progress={context.progressPercentage} />
        )}
        <div className=" py-4 flex-fill overflow-auto">
          {<div className="container h-100">{children}</div>}
        </div>
      </div>
    </LayoutContext.Provider>
  );
};

export default Layout;

Beside this, I created one custom hook in order to use it whenever I want to access layout actions:

 export const useLayoutContext = () => {
  const context = useContext(LayoutContext);

  if (!context)
    throw new Error(
      "No LayoutContext.Provider found when calling useLayoutContext."
    );

  return context;
};

And finally it is very easy to update progress within any element which is inside LayoutContext. Simple as this:

const { setLayoutProgressPercentage, setLayoutProgressVisiblity } =
useLayoutContext();
about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!