Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

129
Visualizações
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 Respostas
Responde à pergunta

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda