Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-magnetic-di

Package Overview
Dependencies
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-magnetic-di

Context driven dependency injection

  • 1.0.0
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
8.4K
increased by16.16%
Maintainers
1
Weekly downloads
 
Created
Source

react-magnetic-di

npm npm bundle size (minified + gzip) License CircleCI codecov

A new take for dependency injection in React for your tests, storybooks and even experiments in production.

  • Nearly-zero performance overhead
  • Works with any kind of dependency (not only components)
  • Targets dependencies at any level of the tree
  • Allows selective injection
  • Enforces separation of concerns
  • Does not mess up with React internals (just uses Context)
  • Can also be enabled in prod, at small cost (off by default)

Philosophy

Dependency injection and component injection for testing purposes is not a new topic. Indeed, the ability to provide a custom implementation of a component/hook while testing or writing storybooks and examples it is extremely valuable.

A common pattern to solve this problem is injecting those "dependencies" in the component via props. However, that approach has a some of downsides:

  1. We are leaking internal implementation details. Props are the public API of a component and we are polluting them with keys that are not relevant for actual consumers, and we are doing that just for our testing needs

  2. Our dependencies are mixed together with other props, which makes them hard to recognise, analyse and might introduce naming conflicts

  3. It introduces additional complexity, for instance when we have spread props down, as we probably don't want pass the dependencies down too

react-magnetic-di takes inspiration from React PropTypes, forcing you to declare the dependencies statically and then using React Context to optionally override those dependencies, with nearly-zero performane overhead when the injection system is off.

Basic usage

npm i react-magnetic-di
# or
yarn add react-magnetic-di

Given a component with complex UI interaction or data dependencies, like a Modal or an Apollo Query, we want to be able integration test it without necessarily test those other dependencies. To achieve that, we define the dependencies on the class component:

import React, { Component } from 'react';
import { provideDependencies } from 'react-magnetic-di';
import { Modal as ModalDI } from 'material-ui';
import { Query as QueryDI } from 'react-apollo';

class MyComponent extends Component {
  static dependencies = provideDependencies({
    Modal: ModalDI,
    Query: QueryDI,
  });

  render() {
    const { Modal, Query } = MyComponent.dependencies();
    return (
      <Modal>
        <Query>{({ data }) => data && 'Done!'}</Query>
      </Modal>
    );
  }
}

Or on our functional component with hooks:

import React, { Component } from 'react';
import { provideDependencies } from 'react-magnetic-di';
import { Modal as ModalDI } from 'material-ui';
import { useQuery as useQueryDI } from 'react-apollo-hooks';

function MyComponent() {
  const { Modal, useQuery } = MyComponent.dependencies();
  const { data } = useQuery();
  return <Modal>{data && 'Done!'}</Modal>;
}

MyComponent.dependencies = provideDependencies({
  Modal: ModalDI,
  useQuery: useQueryDI,
});

Finally, in the integration tests and storybooks we wrap the component with DependencyProvider to override any dependency:

import React from 'react';
import { DependencyProvider } from 'react-magnetic-di';

import { ModalOpen, useQueryMock } from './examples';

storiesOf('Modal content', module).add('with text', () => (
  <DependencyProvider use={{ Modal: ModalOpen, useQuery: useQueryMock }}>
    <MyComponent />
  </DependencyProvider>
));

In the example above we replace all Modal dependencies across all components in the tree with the open version, but that might be wrong for useQuery, as we might want to provide different data to different components. DependencyProvider enables targeted dependency injection via target prop. Together with providers composition it allows multiple, explicit dependency injections:

storiesOf('App', module).add('with text', () => (
  /* replace Modal on all components */
  <DependencyProvider use={{ Modal: ModalOpen }}>
    {/* in MyComponent use one set of data */}
    <DependencyProvider target={MyComponent} use={{ useQuery: useQueryMock }}>
      {/* in MyOtherComponent use a different set of data */}
      <DependencyProvider
        target={MyOtherComponent}
        use={{ useQuery: useQueryOtherMock }}
      >
        <MyApp />
      </DependencyProvider>
    </DependencyProvider>
  </DependencyProvider>
));

Settings

enabled

Defines whenever context replacement is allowed or not. By default is NODE_ENV !== 'production'. It can be enabled also in prod, but it is not recommended.

import { settings } from 'react-magnetic-di';
// only enable during testing
setting.enabled = process.env.NODE_ENV === 'test';

Contributing

To test your changes you can run the examples (with npm run start). Also, make sure you run npm run preversion before creating you PR so you will double check that linting, types and tests are fine.

Keywords

FAQs

Package last updated on 02 Jun 2019

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc