Search code examples
javascriptreactjshigher-order-componentsinfrastructure

React Architecture: I have a common middle component that needs to render custom child components


I'm making a dashboard that uses a common grid component. The grid has its own functionality separate and is used in other areas of the app. It needs to render a custom component within each grid item and has an active component that also renders a custom component, these custom components use functions from the Grids parent component, whatever is rendering it, below is how I do it current, but I'm pretty sure there is a better way of doing it.

Parent component that renders grid and passes down components and functions

import Grid from './common/grid'

class dashboard extends Component {

gridItemSpecificFunction() { console.log('success') }
activeFunction() { console.log('success again') }

  render() {

   return <Grid 
             CustomComponent={ CustomComponent }
             ActiveComponent={ ActiveComponent }
             activeFunctions={ {activeFunction} }
             gridItemFunctions={ { gridItemSpecificFunction:this.gridItemSpecificFunction } }
 />

  }

}

Grid that renders custom active and grid items based on data its passed

class Grid extends Component {

render() {

   const {CustomComponent} = this.props

   return (

     <GridWrapper>
       { this.props.dynamicData.map( data => (
       <GridItemWrapper>
         <CustomComponent { ...data } functions={ this.props.gridItemFunctions } />
       </GridItemWrapper>
       ) )

     { active && < ActiveComponent { ...activeData } 
                     functions={ this.props.activeFunctions }/> }

     </GridWrapper>
       }

   )

  }

}

example of custom component that is using function passed through grid item

class CustomComponent extends Component {

render() {

   const {gridItemSpecificFunction} = this.props.functions

   return (

     <div onClick={ gridItemSpecificFunction }>
      { this.props.text }
     <div>
       }

   )

  }

}

Solution

  • You actually doing great, of course there is another way to do this, probably is better just because become easier to modify, so you probably should use Context hook to get this done, so great packages based their functionalities in Context API Hook, so a great approach would be this

    import React, { useContext, createContext, useMemo } from 'react';
    
    const FunctionalityContext = createContext({}); // just leave it as empty state
    
    // create a hook that return the context function
    const useGridFunctions = () => {
      const functions = useContext(FunctionalityContext);
      return functions;
    };
    
    const CustomComponent = () => {
      const functions = useGridFunctions();
    
      return (
        <div>
          <button onClick={functions.gridItemSpecificFunction}>I am a custom component</button>
        </div>
      );
    }
    
    const ActiveComponent = () => {
      const functions = useGridFunctions();
    
      return (
        <div>
          <button onClick={functions.activeFunction}>I am a active component</button>
        </div>
      );
    }
    
    const ParentGrid = ({ functions }) => {
      const functions = useMemo(() => functions, [functions]);
      // the pass functions object to our context provider to get accesso through dom tree
      return (
        <FunctionalityContext.Provider value={functions}>
          <Grid
            CustomComponent={CustomComponent}
            ActiveComponent={ActiveComponent}
          />
        </FunctionalityContext.Provider>
      );
    }
    

    As you can see you still keep your code almost the same, but you are adding a extra layer that will store the functionalities that will be used by components, so Context Api will help you to achieve this as you want.