skip to Main Content

I have this kind of components tree in my react app. My header component includes the navbar component which in turn includes dialog1 and dialog2.
So, I was wondering what is the best way to pass the props [opened1, opened2, handleDialog1, handleDialog2] to the dialog components from header (passing via navbar). Can you tell me the optimal solution?

     header
       |
     navbar
   |        |
dialog1  dialog2

header.js

import React, { useState } from "react";
import { Button, Stack } from "@mui/material";
import Navbar from "components/Navbar";

export default function Header() {
  const [opened1, setOpened1] = useState(false);
  const [opened2, setOpened2] = useState(false);

  const handleDialog1 = () => {
    setOpened1(!opened1);
  };
  const handleDialog2 = () => {
    setOpened2(!opened2);
  };

  return (
    <Stack>
      <Button onClick={handleDialog1}>my first button</Button>
      <Button onClick={handleDialog2}>my second button</Button>
      <Navbar />
    </Stack>
  );
}

navbar.js

import React from "react";
import { Typography, Stack } from "@mui/material";
import Dialog1 from "components/Dialog1";
import Dialog2 from "components/Dialog2";

export default function Navbar() {
  return (
    <Stack>
      <Typography>lorem ipsum</Typography>
      <Typography>lorem ipsum</Typography>
      <Typography>lorem ipsum</Typography>
      <Dialog1 />
      <Dialog2 />
    </Stack>
  );
}

dialog1.js (dialog2.js is an equal component but with different numbers)

import React from "react";
import { Typography, IconButton, Dialog, DialogTitle } from "@mui/material";

export default function Dialog1() {
  return (
    <Dialog open={opened1} onClose={handleDialog1}>
      <DialogTitle>
        <Typography>my first dialog</Typography>
        <IconButton onClick={handleDialog1} />
      </DialogTitle>
    </Dialog>
  );
}

4

Answers


  1. Personally I use a redux store, it can be implemented for three component but have much more possibilities
    Here you have a good article which explain how to use it
    https://www.freecodecamp.org/news/how-to-manage-state-in-a-react-app/

    It is the best approach to have self refreshing component and avoid synchronization problems, but your component must be coded taking that it count using the useEffect hook for example

    Login or Signup to reply.
  2. I would suggest creating a context and using the useContext hook. It is a more manageable approach than passing props and requires less implementation than redux.

    Here’s the context with your buttons integrated:

    import { FC, createContext, useState } from "react";
    
    export type FilterContextType = { 
        opened1: boolean;
        opened2: boolean;
    };
    
    export const FilterContext = createContext<FilterContextType | null>(null);
    
    export const FilterProvider: FC<{children: any}> = 
        ({children }) => {
    
        const [opened1, setOpened1] = useState(false);
        const [opened2, setOpened2] = useState(false);
    
        return (
            <FilterContext.Provider value={{opened1: opened1, opened2: opened2}}>
                <div>
                    <Button onClick={() => setOpened1(!opened1)}>my first button</Button>
                    <Button onClick={() => setOpened2(!opened2)}>my second button</Button>
                </div>
                {children}
            </FilterContext.Provider>
        );
    };
    

    Then you would wrap you child components in Header component:

    return (
        <Stack>
            <FilterProvider>
                <Navbar />
            </FilterProvider>
        </Stack>
      );
    

    And then use it in any child components:

    const {opened1, opened2} = useContext(FilterContext) as FilterContextType;
    
    Login or Signup to reply.
  3. You could pass the props all the way down manually, but that can be tedious. To avoid this prop drilling for a large amount of nesting, you can use a context.

    Below is an example of how you could implement this (with some unchanged code omitted for brevity).

    header.js:

    import { useState, createContext } from 'react';
    // ...
    export const DialogContext = createContext(null); // you can also move this to a separate file
    // ...
    export default function Header() {
        // ...
        
        return (
            <DialogContext.Provider value={{opened1, handleDialog1, opened2, handleDialog2}}>
                <Stack>
                  <Button onClick={handleDialog1}>my first button</Button>
                  <Button onClick={handleDialog2}>my second button</Button>
                  <Navbar />
                </Stack>
            </DialogContext.Provider>
        );
    }
    

    dialog1.js:

    // ...
    import { useContext } from 'react';
    import { DialogContext } from './header.js';
    export default function Dialog1() {
        const {opened1, handleDialog1} = useContext(DialogContext);
        return (
            <Dialog open={opened1} onClose={handleDialog1}>
              <DialogTitle>
                <Typography>my first dialog</Typography>
                <IconButton onClick={handleDialog1} />
              </DialogTitle>
            </Dialog>
        );
    }
    
    Login or Signup to reply.
  4. I suggest u look into react state management either with the default hooks or with libraries like redux or zustand

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search