Dependency Injection (DI)
Dependency Injection (DI) allows you to split React components into separate versions and comfortably switch them in the project whenever needed, e.g., to make a specific bundle.
DI package helps to solve similar tasks with minimum effort:
- decouple desktop and mobile versions of a component
- implement an experimental version of a component alongside the common one
Install
npm i @bem-react/di -S
Quick start
Note! This example uses @bem-react/classname package.
E.g., for a structure like this:
Components/
Header/
Header@desktop.tsx
Header@mobile.tsx
Footer/
Footer@desktop.tsx
Footer@mobile.tsx
App.tsx
First, create two files that define two versions of the App and use different sets of components: App@desktop.tsx
and App@mobile.tsx
. Put them near App.tsx
.
In each App version (App@desktop.tsx
and App@mobile.tsx
) we should define which components should be used.
Three steps to do this:
- Create a registry with a particular id:
const registry = new Registry({ id: cnApp() });
- Register all the needed components versions under a descriptive key (keys, describing similar components, should be the same across all the versions):
registry.set('Header', Header);
registry.set('Footer', Footer);
- Export the App version with its registry of components:
export const AppNewVersion = withRegistry(registry)(AppCommon);
The files should look like this:
1. In App@desktop.tsx
import { cn } from '@bem-react/classname';
import { Registry, withRegistry } from '@bem-react/di';
import { App as AppCommon } from './App';
import { Footer } from './Components/Footer/Footer@desktop';
import { Header } from './Components/Header/Header@desktop';
const cnApp = cn('App');
const registry = new Registry({ id: cnApp() });
registry.set('Header', Header);
registry.set('Footer', Footer);
export const AppDesktop = withRegistry(registry)(AppCommon);
2. In App@mobile.tsx
import { cn } from '@bem-react/classname';
import { Registry, withRegistry } from '@bem-react/di';
import { App as AppCommon } from './App';
import { Footer } from './Components/Footer/Footer@mobile';
import { Header } from './Components/Header/Header@mobile';
const cnApp = cn('App');
const registry = new Registry({ id: cnApp() });
registry.set('Header', Header);
registry.set('Footer', Footer);
export const AppMobile = withRegistry(registry)(AppCommon);
Time to use these versions in your app dynamically!
If in App.tsx
your dependencies were static before
import React from 'react';
import { cn } from '@bem-react/classname';
import { Header } from './Components/Header/Header';
import { Footer } from './Components/Footer/Footer';
export const App = () => (
<>
<Header />
<Footer />
</>
);
Now the dependencies can be injected based on the currently used registry
with ComponentRegistryConsumer
import React from 'react';
import { cn } from '@bem-react/classname';
import { ComponentRegistryConsumer } from '@bem-react/di';
const cnApp = cn('App');
export const App = () => (
<ComponentRegistryConsumer id={cnApp()}>
{({ Header, Footer }) => (
<>
<Header />
<Footer />
</>
)}
</RegistryConsumer>
);
with useComponentRegistry
(require react version 16.8.0+)
import React from 'react';
import { cn } from '@bem-react/classname';
import { useComponentRegistry } from '@bem-react/di';
const cnApp = cn('App');
export const App = () => {
const { Header, Footer } = useComponentRegistry(cnApp());
return (
<>
<Header />
<Footer />
</>
);
};
So you could use different versions of your app e.g. for conditional rendering on your server side or to create separate bundles
import { AppDesktop } from './path-to/App@desktop';
import { AppMobile } from './path-to/App@mobile';