Git Product home page Git Product logo

react-magnetic-di's Introduction

magnetic-di logo

magnetic-di

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

  • Close-to-zero performance overhead on dev/testing
  • Zero performance overhead on production (code gets stripped unless told otherwise)
  • Promotes type safety for mocks
  • Works with any kind of value (functions, objects, strings) and in all closures / React components
  • Replaces dependencies at any depth of the call chain / React tree
  • Allows selective injection
  • Enforces separation of concerns, keeps your component API clean
  • Proper ES Modules support, as it does not mess up with modules/require or React internals

Philosophy

Dependency injection and component injection is not a new topic. Especially the ability to provide a custom implementation of a component/hook while testing or writing storybooks and examples it is extremely valuable. magnetic-di takes inspiration from decorators, and with a touch of Babel magic allows you to optionally override imported/exported values in your code so you can swap implementations only when needed.

Usage

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

Adding babel plugin

Edit your Babel config file (.babelrc / babel.config.js / ...) and add:

  // ... other stuff like presets
  plugins: [
    // ... other plugins
    'react-magnetic-di/babel-plugin',
  ],

This is where the magic happens: we safely rewrite the code to prepend di(...) in every function scope, so that the dependency value can be swapped. We recommend to only add the plugin in development/test environments to avoid useless const assignment in production. You can either do that via multiple babel environment configs or by using enabledEnvs option.

Using dependency replacement

Once babel is configured, in your tests you can create type safe replacements via injectable and then use runWithDi , which will setup and clear the replacements for you after function execution is terminated. Such util also handles async code, but might require you to wrap the entire test to work effectively with scheduled code paths, or event driven implementations.

Assuming your source is:

import { fetchApi } from './fetch';

export async function myApiFetcher() {
  const { data } = await fetchApi();
  return data;
}

Then in the test you can write:

import { injectable, runWithDi } from 'react-magnetic-di';
import { fetchApi } from './fetch';
import { myApiFetcher } from '.';

it('should call the API', async () => {
  // injectable() needs the original implementation as first argument
  // and the replacement implementation as second
  const fetchApiDi = injectable(
    fetchApi,
    jest.fn().mockResolvedValue({ data: 'mock' })
  );

  const result = await runWithDi(() => myApiFetcher(), [fetchApiDi]);

  expect(fetchApiDi).toHaveBeenCalled();
  expect(result).toEqual('mock');
});

Using dependency replacement in React tests and storybooks

For React, we provide a specific DiProvider to enable replacements across the entire tree. Given a component with complex UI interaction or data dependencies, like a Modal or an Apollo Query, we want to easily be able to integration test it:

import React from 'react';
import { DiProvider, injectable } from 'react-magnetic-di';
import { Modal } from 'material-ui';
import { useQuery } from 'react-apollo-hooks';

// injectable() needs the original implementation as first argument
// and the replacement implementation as second
const ModalOpenDi = injectable(Modal, () => <div />);
const useQueryDi = injectable(useQuery, () => ({ data: null }));

// test-testing-library.js
it('should render with react-testing-library', () => {
  const { container } = render(<MyComponent />, {
    wrapper: (p) => <DiProvider use={[ModalOpenDi, useQueryDi]} {...p} />,
  });
  expect(container).toMatchSnapshot();
});

// story.js
storiesOf('Modal content', module).add('with text', () => (
  <DiProvider use={[ModalOpenDi, useQueryDi]}>
    <MyComponent />
  </DiProvider>
));

In the example above we replace all Modal and useQuery dependencies across all components in the tree with the custom versions. If you want to replace dependencies only for a specific component (or set of components) you can use the target prop:

// story.js
storiesOf('Modal content', module).add('with text', () => (
  <DiProvider target={[MyComponent, MyOtherComponent]} use={[ModalOpenDi]}>
    <DiProvider target={MyComponent} use={[useQueryDi]}>
      <MyComponent />
      <MyOtherComponent>
    </DiProvider>
  </DiProvider>
));

Here MyComponent will have both ModalOpen and useQuery replaced while MyOtherComponent only ModalOpen. Be aware that target needs an actual component declaration to work, so will not work in cases where the component is fully anonymous (eg: export default () => ... or forwardRef(() => ...)).

The library also provides a withDi HOC in case you want to export components with dependencies already injected:

import React from 'react';
import { withDi, injectable } from 'react-magnetic-di';
import { Modal } from 'material-ui';
import { MyComponent } from './my-component';

const ModalOpenDi = injectable(Modal, () => <div />);

export default withDi(MyComponent, [ModalOpenDi]);

withDi supports the same API and capabilities as DiProvider, where target is the third argument of the HOC withDi(MyComponent, [Modal], MyComponent) in case you want to limit injection to a specific component only.

When you have the same dependency replaced multiple times, there are two behaviours that determine which injectable will "win":

  • the one defined on the closest DiProvider wins. So you can declare more specific replacements by wrapping components with DiProvider or withDi and those will win over same type injectables on other top level DiProviders
  • the injectable defined last in the use array wins. So you can define common injectables but still override each type case by case (eg: <DiProvider use={[...commonDeps, specificInjectable]}>

Other replacement patterns

Allowing globals (variables) replacement

Currently the library does not enable automatic replacement of globals. To do that, you need to manually "tag" a global for replacement with di(myGlobal) in the function scope. For instance:

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

export async function myApiFetcher() {
  // explicitly allow fetch global to be injected
  di(fetch);
  const { data } = await fetch();
  return data;
}

Alternatively, you can create a "getter" so that the library will pick it up:

export const fetchApi = (...args) => fetch(...args);

export async function myApiFetcher() {
  // now injection will automatically work
  const { data } = await fetchApi();
  return data;
}

Ignoring a function scope

Other times, there might be places in code where auto injection is problematic and might cause infinite loops. It might be the case if you are creating an injectable that then imports the replacement source itself.

For those scenarios, you can add a comment at the top of the function scope to tell the Babel plugin to skip that scope:

import { fetchApi } from './fetch';

export async function myApiFetcher() {
  // di-ignore
  const { data } = await fetchApi();
  return data;
}

Tracking unused injectables

By default magnetic-di does not complain if an injectable is not used or if a dependency has not being replaced. In large codebases however, that might led to issues with stale, unused injectables or with lack of knowledge in what could be replaced. To ease introspection, the library provides a stats API that returns unused injectables.

  • stats.unused() returns an array of entries { get(), error() } for all injectables that have not been used since stats.reset() has been called

This is an example of stats guard implementation using the returned error() helper:

import { stats } from 'react-magnetic-di';

beforeEach(() => {
  // it's important to reset the stats after each test
  stats.reset();
});
afterEach(() => {
  stats.unused().forEach((entry) => {
    // throw an error pointing at the test with the unused injectable
    throw entry.error();
  });
});

Configuration Options

Babel plugin options

The plugin provides a couple of options to explicitly disable auto injection for certain paths, and overall enable/disable replacements on specific environments:

  // In your .babelrc / babel.config.js
  // ... other stuff like presets
  plugins: [
    // ... other plugins
    ['react-magnetic-di/babel-plugin', {
      // List of paths to ignore for auto injection. Recommended for mocks/tests
      exclude: ['mocks', /test\.tsx?/],
      // List of Babel or Node environment names where the plugin should be enabled
      enabledEnvs: ['development', 'test'],

    }],
  ],

injectables options

When creating injectables you can provide a configuration object to customise some of its behaviour. • displayName: provide a custom name to make debugging easier:

const fetchApiDi = injectable(fetchApi, jest.fn(), { displayName: 'fetchApi' });

target: allows a replacement to only apply to specific function(s):

const fetchApiDi = injectable(fetchApi, jest.fn(), { target: fetchProjects });

track: skip reporting it in stats.unused() (handy if you provide default injectables across tests):

const fetchApiDi = injectable(fetchApi, jest.fn(), { track: false });

global: allows a replacement to be available everywhere, at any point, until DiProvider unmounts (alternatively use global prop on DiProvider to make all use replacements act globally):

const fetchApiDi = injectable(fetchApi, jest.fn(), { global: true });

DiProvider props

use: required prop, it is an array of replacements • target: allows a replacement to only apply to specific components(s) • global: boolean, allows replacements to be available outside the render phase

ESLint plugin and rules

In order to enforce better practices, this package exports some ESLint rules:

rule description
order enforces di(...) to be the top of the block, to reduce chances of partial replacements
no-duplicate prohibits marking the same dependency as injectable more than once in the same scope
no-extraneous enforces dependencies to be consumed in the scope, to prevent unused variables
no-restricted-injectable prohibits certain values from being injected: paths: [{ name: string, importNames?: string[], message?: string }]
sort-dependencies require injectable dependencies to be sorted

The rules are exported from react-magnetic-di/eslint-plugin. Unfortunately ESLint does not allow plugins that are not npm packages, so rules needs to be imported via other means for now.

Current limitations

  • DiProvider does not support dynamic use and target props (changes are ignored)
  • Does not replace default props (or default parameters in general): so dependencies provided as default parameters (eg function MyComponent ({ modal = Modal }) { ... }) will be ignored. If you accept the dependency as prop/argument you should inject it via prop/argument, as having a double injection strategy is just confusing.
  • Injecting primitive values (strings, booleans, numbers, ...) can be unreliable as we only have the actual value as reference, and so the library might not exactly know what to replace. In cases where multiple values might be replaced, a warning will be logged and we recommend you declare an inject a getter instead of the value itself.
  • Targeting only works on named functions/classes, so it won't work on anonymous scopes (eg export default () => { ... } or memo(() => { ... }))
  • If you define an injectable as global then you lose the ability to "scope" that injectable to a section of the tree, so the override will apply "globally". As a result, when defining multiple global replacements for the same dependency, only the last one evaluated will apply. So be mindful when using it in a multi DiProvider setting.

FAQ

Cannot seem to make injectable work

A way to check if some dependency has been tagged for injection is to use the debug util, as it will print all values that are available for injection:

import { debug } from 'react-magnetic-di';
// ...
console.log(debug(myApiFetcher));
// It will print ['fetchApi']

One possible reason for it to happen is that the context has been lost. Typical occurrences are async or deeply nested functions (especially in React). The solution is setting the prop global on DiProvider (or the same injectable config) to better handle those scenarios (but refrain from abusing it).

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.

react-magnetic-di's People

Contributors

albertogasparin avatar auzwang avatar dependabot[bot] avatar pawk3k avatar sean-cullinan avatar timeraider avatar yamadapc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

react-magnetic-di's Issues

Mocking not functional with Jest + storybook + storyshots

Hi, I'm trying to use react-magnetic-di in a project created using https://github.com/facebook/create-react-app along with Jest and storybook snapshots.

For some reason the mocking does not appear to work. Apologies in advance if I have misunderstood something, I do not understand the whole development stack very well.

Is the Babel plugin critical for the functioning of magnetic-di, or is it optional? It seems create-react-app out of the box does not allow custom Babel plugins.

In any case, I tried ejecting from create-react-app and created babel.config.json with:

{
  "plugins": [["react-magnetic-di/babel-plugin", {"forceEnable": true}]]
}

Here's my test case:

import {di, DiProvider} from 'react-magnetic-di';
import React from 'react';

const useBla = () => 'real';

const useBlaMock = di.mock(useBla, () => 'mocked');

const Bla: React.FC = () => {
  di(useBla);
  const value = useBla();
  return <div>{value}</div>;
};

storiesOf('DiTest', module).add('bla', () => (
  <DiProvider use={[useBlaMock]}>
    <Bla />
  </DiProvider>
));
// ^^^^^^^^^^^ test case

The snapshot output is:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots DiTest bla 1`] = `
<div>
  real
</div>
`;

So the mocked return value of mocked was not used.

eslint rule "react-magnetic-di/no-extraneous" unnecessarily injects Components provided via HOC

In this example Component is injected via HOC, but react-magnetic-di/no-extraneous eslint rule considers it to be injectable via DI

export const withNestingOptimisation = <Props: { children?: Node, ... }, Ref>(
    Component: AbstractComponent<Props, Ref>,
) => {
    const WithNestingOptimisation = forwardRef<Props, Ref>(
        ({ children = null, ...rest }: Props, ref) => {
            // "Component" here should not be DI'ed
            di(Component, useShouldNestedElementRender);

            return true ? (
                <Component {...rest} ref={ref}>
                    {children}
                </Component>
            ) : (
                children
            );
        },
    );

    return WithNestingOptimisation;
};

Issues with CRA and TypeScript

Hi Alberto,

thanks for the project, looks very cool!
I was trying it out on a React app bootstrapped with the TypeScript template and when I import the library like so:

import { di } from "react-magnetic-di/macro";

I get: Cannot find module 'react-magnetic-di/macro' or its corresponding type declarations.

I guess it makes sense since they seem to be missing when you import it like that.

Anyway, using the library with TS doesn't seems to be working properly for me.

For example if you include the doc example for testing with RTL it complains about missing required children prop (as per your types):

const { container } = render(<MyComponent />, {
    wrapper: (p) => <DiProvider use={[ModalOpenMock, useQueryMock]} {...p} />,
  });

Should be:

const { container } = render(<MyComponent />, {
    wrapper: (p: {children?: React.ReactNode}) => <DiProvider use={[ModalOpenMock, useQueryMock]}  children={p.children} />,
  });

It's just a documentation issue but will probably trip other users.

I wasn't able to run it in the end unfortunately but maybe because of the first error above that results in Warning: Seems like you are using react-magnetic-di without Babel plugin... if I import without /macro and Cannot find module 'react-magnetic-di/macro' from '/Users/XXXX/repos/repo/src/components/AdminMessages' if I add //@ts-ignore to it.

Thanks!

Enzyme `wrappingComponent` doesn't work

This recommended form for enzyme doesn't actually work for some reason:

  const container = mount(<MyComponent />, {
    wrappingComponent: DiProvider,
    wrappingComponentProps: { use: [ModalOpen, useQuery] },
  });

No errors, the context is just not provided.

Works okay if you do this though:

const container = mount(
  <DiProvider use={[ModalOpen, useQuery]}>
    <MyComponent />
  </DiProvider>
);

Add option to strip code on production build

Currently, even on prod builds, we have variable assignment and one function call (a pass through). We can optionally strip the entire di(...) so the performance cost in prod is actually zero.

Eslint errors on files without di

Seems like this code breaks eslint on the JSX tag:

// @flow strict-local

import React from 'react';
import { shallow } from 'enzyme';

import type { Props } from './types';
import CommitWarningNotification from './view';

test('Should render commit warning notifications', () => {
    const props: Props = {
        show: true,
        successfulCommitCount: 8,
        failedCommitCount: 7,
        onClose: () => {},
    };
    const tree = shallow(<CommitWarningNotification {...props} />);
    expect(tree).toMatchSnapshot();
});
Cannot read property 'name' of undefined Occurred while linting ....

Probably due to missing check on no-extraneous JSXOpeningElement:exit for diIdentifier presence

Babel plugin cannot be found

The babel plugin 'cannot be found' at the moment if using react-magnetic-di/babel in babel config.

Workaround: reference the full babel plugin path:

  // ... other stuff like presets
  plugins: [
    // ... other plugins
    'react-magnetic-di/lib/cjs/babel',
  ],

Is storybook 7 supported?

I have a sneaky suspicion that I might be doing something wrong, but I copy/pasted the examples on the readme and shooting console.log(debug(ModalOpenDi)); seems to always come back with undefined. I am using react native and storybook 7, so I wonder if maybe my setup is not supported somehow?

Add support for `react-test-renderer`

Dependency injection doesn't work properly when a component is being rendered via react-test-renderer:

import React, { useState } from 'react';
import { create } from 'react-test-renderer';
import { DiProvider, injectable } from 'react-magnetic-di';

import { Input, useTheme } from '../input';

const useThemeDi = injectable(useTheme, () => {
  return useState({ color: '#B00' });
});

describe('Input', () => {
    it('should render with theme', () => {
      const tree = create(
        <DiProvider use={[useThemeDi]}>
          <Input />
        </DiProvider>
      ).toJSON();
      expect(tree).toMatchInlineSnapshot(`
        <input
          placeholder="Type..."
          style={
            {
              "border": "1px solid #777", // <-- Wrong, should be #B00
            }
          }
        />
      `);
    });
  });
});

Supposedly due to how react-test-renderer's create works, the di consumer does not get a value from a Provider in

const { getDependencies = (v) => v } = Context._currentValue || {};
and ends up using a default implementation from
getDependencies(deps) {
return deps;
},

Replacing const { getDependencies = (v) => v } = Context._currentValue || {} with const { getDependencies = (v) => v } = useContext(Context) works, but breaks the ability to use di in both functional and class components.

Would you consider a major version release to remove the hack in di function and providing di and useDi separately?

Error: "Invalid hook call. Hooks can only be called inside of the body of a function component."

I'm seeing the following error whenever I use <DiProvider /> inside jest:

Invalid hook call. Hooks can only be called inside of the body of a function component.

Screen Shot 2021-11-16 at 5 02 59 PM

Screen Shot 2021-11-16 at 5 04 52 PM

I thought I was doing something wrong but I tried this by building an MVP:

// jest spec
// SomeTest.spec.js
import { useState } from 'react'
import { DiProvider, di, injectable } from 'react-magnetic-di'
import { render } from '@testing-library/react'
import '@testing-library/jest-dom'

function useSomeHook() {
  return useState('dummy')
}

function Testing() {
  di(useSomeHook)
  const [dummy] = useSomeHook()
  return <div>{dummy}</div>
}

const useSomeHookDi = injectable(useSomeHook, () => ['testing'])
function WithDep() {
  return (
    <DiProvider use={[useSomeHookDi]}>
      <Testing />
    </DiProvider>
  )
}

test('loads and displays input text', async () => {
  const { getByText } = render(<WithDep />)
  expect(() => getByText('testing')).not.toThrow()
})

Track used and unused mocks

There is a general problem with mocking - it can be only added.

  • Let's imagine you have a component doing something
  • You create a test for it...
    • and test fails
  • You add a mock
    • and test passes
  • Time goes by, component got refactored a few time
    • but the no longer required mock is still here

– A short story "How I met code erosion"


Desired goal: be notified about missing mocks as well as no longer required ones.

Proposal: implement tracking of injectable usage:

  • verify that every injected got di-ed.
    • Will reduce wtf-factor of missed di(if eslint by any reason is not working). Still happening once-twice a week in my team.
  • verify that some injected functions(only) got executed
    • is totally a jest responsibility, but magnetic should guide which injectables expected.toHaveBeenCalled and which are not.
    • not all injectables should be executed, there are cased for di-ing something on environment level (intl), or some tests where stuff is excepted not to be called.

I am not sure that Magnetic can do what is required due to the need of integration with the test runner, or at least with stubs (aka jest.fn). Thus desired goal might be achievable only by building an abstraction on top of Magnetic.

Deciding what should be build inside Magnetic and what should be outside - is the task for this issue.

Is it possible to update an injectable, i.e. make it dynamic?

Hi, I encountered a specific scenario in which I need to make a prop provided by the mocked hook dynamic, here is the code:

  passedProps: SelectRoomPageUIProps & { selectResort: ResortComparisonDropdownProps },
) => {
  const [selectedPropertyId, setSelectedPropertyId] = React.useState('2');
  const [selectedCardId, setRoomRateCardId] = React.useState('2');

  const onChange = useCallback(
    (id: any) => {
      setSelectedPropertyId(id);
    },
    [setSelectedPropertyId],
  );

  const useResortControllerDi = injectable(useResortController, () => ({
    apolloResult: {
      loading: false,
    } as ApolloResult,
    uiProps: {
      ...passedProps.selectResort,
      selectedPropertyId,
      onChange: onChange,
    },
  }));

  return (
    <DiProvider use={[useResortControllerDi]}>
      <SelectRoomPageUI
        {...passedProps}
      />
    </DiProvider>
  );
};

As you can notice the value of selectedPropertyId is dynamic, the reason I ended up needing to mock the hook is because there's a component nested under SelectRoomPageUI that consumes from the hook directly(the idea was to avoid prop drilling), thanks to your library I fixed it and the story now renders the list of values being mocked properly on the nested component, but when I trigger the onChange method it does update and if I use useEffect to watch the selectedPropertyId value it reflects it properly on the console but the value returned by useResortControllerDi never updates.

If I'm doing this wrong please feel free to correct me, and thanks again for making this library it saved me a lot of time of r&d.

Is it possible to use di() in parent components only?

Hi there, loving this package, thank you! I have a question - am I right in assuming this sentence here means that you can make the di() call in a parent component and it will replace all usages in child components?

In the example above we replace all Modal and useQuery dependencies across all components in the tree with the custom versions.

I'm trying to create a wrapper component for storybook so that all the di code can be contained in the story itself, and am testing this in a wrapper component, but am thinking I might've misinterpreted the sentence above. For example, this is my storybook for the component.

// Header.stories.jsx
import { useState } from "react";
import { DiProvider, injectable } from "react-magnetic-di";
import Header from "./Header";

const useStateDi = injectable(useState, () => ["hello", () => null]);

export default {
  title: "My Components/Header",
  component: Header,
};

const Template = () => (
  <DiProvider use={[useStateDi]}>
    <Header />
  </DiProvider>
);

export const Default = Template.bind({});

And then, in my actual component, if I'm looking to replace useState for example, can I call di(useState) in a component that wraps the actual useState usage?

import { useState } from "react";
import { di } from "react-magnetic-di";

const ChildComponent = () => {
  const [title, _setTitle] = useState("Hi there");
  return <h2>{title}</h2>;
};

const Header = () => {
  di(useState);
  return <ChildComponent />;
};

export default Header;

missing React hooks exemption for exhaustive-inject

The rule contains a few react hooks - useState, useContext, useReducer - but not any other.

https://github.com/albertogasparin/react-magnetic-di/blob/master/src/eslint/rules/exhaustive-inject.js#L16

Is it a feature or a bug? I can understand if someone wants to override useEffect, but am a little concerned about useRef
In fact useState is overridden the most.

However adding any exemptions to the rule may cause friction to the testing process as something you might expect to be overridden will not be.

unable to `di` in production mode

Hi, thank you for this amazing work!

I want to do dependency injection on production mode (as for mocking data, for now), and i have added these lines in my .babelrc

plugins: [
    ["react-magnetic-di/babel-plugin", { "forceEnable": true }],
  ],

the problem was in production mode, di was not applied. And if it helps, i cannot import things from react-magnetic-di even after i put those line above in my .babelrc.

is there something i did miss?

Thank you.

DiProvider prop-types does not support forwardRef replacement

Issue

The DiProvider prop-types use definition prevents forward refs from being used, and thus produces a warning when using the provided withDi utility. This issue is more problematic in environments that treat any console warnings as errors.

Steps to reproduce the issue

  1. Create a component that injects a dependency where its value utilises a forward ref, particularly from the provided withDi utility
| src/app/examples.js

export const DefaultApp = withDi(App, [injectable(Foo, DefaultFoo)]);
| src/foo/examples.js

// DefaultFoo will produce the warning
export const DefaultFoo = withDi(Foo, [
  injectable(Container, ({ children }) => <div>injected {children}</div>)
]);

// or: export const DefaultFoo = withDi(Foo);

Reproducible example here: react-magnetic-di-sandbox

What's the expected result?

  • The component is injected correctly, with no prop-types warnings

What's the actual result?

  • The component is injected correctly, with a prop-type warning:

Warning: Failed prop type: Invalid prop use[0] of type object supplied to DiProvider, expected function.
at DiProvider (https://yh3sc.csb.app/node_modules/react-magnetic-di/lib/esm/react/provider.js:29:23)
at withDi(App)

Unbound Magnetic from React

Magnetic is mostly used in a preconfigured testing environment with so many things expected to be "global". Having this in mind what would one think about adding alternative way to configure di - via global configuration, not ContextApi

That will allow using di for plain functions, not components/hooks. One can do (and doing) this today by testing such functions as hooks, which is not the correct way.

All required is to wire a configuration variable to https://github.com/albertogasparin/react-magnetic-di/blob/master/src/react/consumer.js#L14

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.