Home > OS >  React - Composition VS Configuration
React - Composition VS Configuration

Time:07-01

I am currently working on a project that requires dynamically injecting one component into another.

My project is using Redux, so I came up with two possible solutions which both have their advantages and disadvantages, but I don't know which one to choose. I know that by nature, React encourages composition, but I'm still curious to know if the second approach (simpler and faster to use) is still good :

export const SlideOverComponents = {
  'UserCreate': UserCreate,
  'UserUpdate': UserUpdate,
};

The idea is to register all components that can be injected as a key value pair, and dispatch a Redux action with the key and the props required by this component.

{(!!componentKey && !!SlideOverComponents[componentKey]) && React.createElement(SlideOverComponents[componentKey], props)}

Then in my parent container, I just read this key and use the React.createElement to display the injected one.

This solution is working fine and is easy and fast to use because I just have to register any new component to the object to make it work.

Is this approach "ok" ? Or should I use composition ?

(I'm asking from a "good practice" or "anti-pattern" point of view.)

CodePudding user response:

Yes that's fine, as long as the interface between all of the SlideOverComponents are completely identical. Your code is more verbose than it needs to be. You don't need createElement either if you assign it to a variable first

const Component = SlideOverComponents[componentKey]
return (
  <div>
    {Component && <Component {...props} />}
  </div>
)

CodePudding user response:

You can still use Composition for this and create some kind of check or switch statement inside the "Generic" Component. That way you could avoid adding so many checks(ifs) outside of the parent component and guarantee that eventually non-existing keys could fallback to a default behavior or even to an error.

There are several ways of implementing it but one using switch that I like is this one:

function UserInteraction({ key, ...props }) {
  switch (key) {
    case "create": {
      return <UserCreate {...props} />;
    }
    case "update": {
      return <UserUpdate {...props} />;
    }
    default: {
      return null;
      // or you could thrown an error with something like
      throw new Error(`Error: key ${key} not present inside component User`);
    }
  }
}

You could also use the Object.keys() method to accomplish almost the same behavior:

const UserInteractionOptions = {
  "create": UserCreate,
  "update": UserUpdate,
}

function UserInteraction({ key, ...props }) {
  if (!Object.keys(UserInteractionOptions).includes(key)) {
    return null;
    // or you could thrown an error with something like
    throw new Error(`Error: key ${key} not present inside component User`);
  } 

  const InteractionComponent = UserInteractionOptions[key];
  
  return <InteractionComponent {...props} />;
}

The main idea is to isolate the logic from deciding which component to render (and if it can be rendered) inside that component.

For future reading, you could check on TypeScript and how this can be easily handled by types, coercion, and the checks for non-present keys could be made before even the code runs locally.


A little of nitpicking: you are not "injecting" a Component inside another Component. You are just passing a key to deciding if the Parent Component renders or not the Child component through a flag. The injection of one Component into another involves passing the full component as a prop and just rendering it (or customizing it, eventually). You could look at how React decides to render the children prop and how it decides if it is null, a string, or a ReactComponent to render an actual component. Also, a good topic to research is Dependency Injection.

As a simple example, injecting a component could looks like this:

function Label({ text }) {
  return <p>{text}</p>;
}

function Input({ Label, ...props }) {
  return (
    <div>
      <Label />
      <input {...props} />
    </div>
  );
}
  • Related