Search code examples
javascriptreactjsredux

In how many ways we can pass props to all child components in react


I have an app code

import React from "react";
import { Route, Switch } from "react-router-dom";
import Minidrawer from './components/Drawer/Minidrawer'
import { makeStyles } from '@mui/styles';
import Box from '@mui/material/Box';
import Main from "./components/Main/Main";
import {useSelector} from 'react-redux'


const useStyles = makeStyles({
  container: {
    display: "flex"
  }
});

export default function App() {
  const classes = useStyles();
  const user = useSelector((state) => state.auth);

  return (
    <Box sx={{ display: 'flex' }}>
      <Minidrawer currUser={user}/>
      <Switch>
        <Route exact from="/" render={props => <Main childText="home" currUser={user} {...props} />} />
        <Route exact path="/auth" render={props => <Main childText="auth" currUser={user} {...props} />} />
        <Route exact path="/register-client" render={props => <Main childText="registerClient" currUser={user} {...props} />} />
      </Switch>
      </Box>
  );
}

I have to pass currUser to all child components imported in App but I do not want to duplicate the code, what are different ways to achieve this so that all of the components have access to currUser?


Solution

  • if I understand what you want to do, you want to pass props to all children of a component, if the components are simple components you can do as follows:

    import React from "react";
    import Main from "./Main";
    import PassPropsToNormalComponents from "./PassPropsToNormalComponents";
    
    export default function App() {
      const user = {
        username: "lakhdar"
      };
    
      return (
        <div style={{ display: "flex" }}>
          <PassPropsToNormalComponents currUser={user}>
            <Main childText="home" />
            <Main childText="auth" />
            <Main childText="registerClient" />
          </PassPropsToNormalComponents>
        </div>
      );
    

    and this is the PassPropsToNormalComponents file

    import React from "react";
    
    export default function PassPropsToNormalComponents({ children, ...props }) {
      const childrenWithProps = React.Children.map(children, (child) => {
        if (React.isValidElement(child)) {
          return React.cloneElement(child, { ...child.props, ...props });
        }
        return child;
      });
    
      return <>{childrenWithProps}</>;
    }
    

    but in your case passing the props to the routes wont' make the routes pass the props to their rendered components so we need an extra step here:

    first the file where we provide the props to the parent:

    import React from "react";
    import { Route, Switch } from "react-router-dom";
    import Main from "./Main";
    import PassPropsToRouteComponents from "./PassPropsToRouteComponents";
    
    export default function App() {
      const user = {
        username: "lakhdar"
      };
    
      return (
        <div style={{ display: "flex" }}>
          <Switch>
            <PassPropsToRouteComponents currUser={user}>
              <Route
                exact
                from="/"
                render={(props) => {
                  return <Main childText="home" {...props} />;
                }}
              />
              <Route
                exact
                path="/auth"
                render={(props) => <Main childText="auth" {...props} />}
              />
              <Route
                exact
                path="/register-client"
                render={(props) => <Main childText="registerClient" {...props} />}
              />
            </PassPropsToRouteComponents>
          </Switch>
        </div>
      );
    }
    

    and finally, the extra step is to get the rendered element and pass it its own props + the props from the parent, and the file looks like this:

    import React from "react";
    
    export default function PassPropsToRouteComponents({ children, ...props }) {
      const childrenWithProps = React.Children.map(children, (child) => {
        if (React.isValidElement(child)) {
          const routerChild = child.props.render();
          return React.cloneElement(child, {
            ...child.props,
            render: () => {
              return React.cloneElement(routerChild, {
                ...routerChild.props,
                ...props
              });
            }
          });
        }
        return child;
      });
    
      return <>{childrenWithProps}</>;
    }
    

    link to working codesandbox: https://codesandbox.io/s/gracious-meadow-dj53s

    I hope this is what you've been looking for.