Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

131
Vistas
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 Respuestas
Responde la pregunta

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 Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda