Installations
npm install react-decoupler-react-18
Developer Guide
Typescript
No
Module System
CommonJS
Node Version
22.9.0
NPM Version
10.8.3
Releases
Unable to fetch releases
Contributors
Unable to fetch Contributors
Languages
JavaScript (93.13%)
HTML (4.46%)
CSS (2.41%)
validate.email 🚀
Verify real, reachable, and deliverable emails with instant MX records, SMTP checks, and disposable email detection.
Developer
SERitter
Download Statistics
Total Downloads
214
Last Day
1
Last Week
1
Last Month
13
Last Year
214
GitHub Statistics
MIT License
94 Commits
2 Branches
1 Contributors
Updated on Oct 29, 2024
Bundle Size
11.28 kB
Minified
4.07 kB
Minified + Gzipped
Package Meta Information
Latest Version
1.1.3
Package Id
react-decoupler-react-18@1.1.3
Unpacked Size
387.19 kB
Size
63.87 kB
File Count
12
NPM Version
10.8.3
Node Version
22.9.0
Published on
Oct 10, 2024
Total Downloads
Cumulative downloads
Total Downloads
214
Last Day
0%
1
Compared to previous day
Last Week
0%
1
Compared to previous week
Last Month
8.3%
13
Compared to previous month
Last Year
0%
214
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Dependencies
2
Peer Dependencies
1
Dev Dependencies
40
React Decoupler
Note
The original project is available at https://www.npmjs.com/package/react-decoupler, and is the work of Tommy Groshong. Unfortunately, the package hasn't been updated in a long time and the github repository has been archived by the owner. I still need to use this in some legacy projects that I am working on, and so I have done a very simple update to the dependencies and configuration to allow it to be used with React 18. The existing tests are all running and passing and things seem to be working. I make no garuantees that this is a stable or reliable upgrade however.
Overview
React Decoupler is a simple dependency injection / service locator utility designed to help you decouple your React components from outside concerns and make it easier to reuse, refactor, and test your code.
Installation
- NPM:
npm install --save react-decoupler
- Yarn:
yarn add react-decoupler
Explanation
How simple is it? RIDICULOUSLY SIMPLE! No Really. It's just a simple data structure passed down via React Context that maps "service keys" to "services", all wrapped in an ergonomic API with a bunch of helpful react-specific hooks and components to make accessing it easier.
Why would you use this? Because you are busy, and this saves you the effort of writing the few hundred lines of code, glue, and tests to provide the same behavior and helpful API.
Examples
Simple Example
1import React from 'react'; 2import ReactDOM from 'react-dom'; 3import { DecouplerProvider, useServices } from 'react-decoupler'; 4 5const serviceMap = { 6 helloworld: name => `Hello, ${name ? name : 'World'}!`, 7}; 8 9function App() { 10 const [getGreeting] = useServices(['helloWorld']); 11 return ( 12 <div> 13 <span>{getGreeting()}</span> 14 <span>{getGreeting('Foo')}</span> 15 </div> 16 ); 17} 18 19ReactDOM.render( 20 <DecouplerProvider services={serviceMap}> 21 <App /> 22 </DecouplerProvider>, 23 document.getElementById('app') 24);
Alternate Example
1import React from 'react'; 2import ReactDOM from 'react-dom'; 3import { 4 DecouplerProvider, 5 ServiceLocator, 6 useServices, 7} from 'react-decoupler'; 8import apiClient from './myApiClient'; 9 10const locator = new ServiceLocator(); 11locator.register('apiClient', apiClient); 12 13function App() { 14 const [apiClient] = useServices(['apiClient']); 15 const [loading, setLoading] = React.useState(false); 16 const [data, setData] = React.useState([]); 17 18 React.useEffect(() => { 19 apiClient 20 .loadData() 21 .then(setData) 22 .finally(() => setLoading(false)); 23 }, [apiClient]); 24 25 if (loading) { 26 return <div>Loading...</div>; 27 } 28 29 return <div>{/* do stuff with data*/}</div>; 30} 31 32ReactDOM.render( 33 <DecouplerProvider locator={locator}> 34 <App /> 35 </DecouplerProvider>, 36 document.getElementById('app') 37);
Big Example
See example/ directory.
API Reference
ServiceLocator
A JavaScript Class that implements service registration and resolution.
Public Methods
class ServiceLocator
static fromServices(services)
register(key, service, options)
resolve(dependencies)
clearDependencyCache()
-
static fromServices(services: {})
: Factory function to create a ServiceLocator instance filled with the services from the provided vanilla JS object. Each key-value entry of the service object becomes a registered service in the locator. -
register(key, service, options = {})
: Register a single service with a given key. Any value may be used as a key or a service. Supported options:-
allowOverwrite: Boolean
: When true, will replace any existing service registration using the same key. An error is thrown if a key is already registered and then a client attempts to re-register without this option. -
withParams: Array<any>
: Binds the given array of parameters as arguments to the service (value of the service must be a callable that supports.bind()
). The binding happens at first-resolve-time and is consistent between calls. Use in conjunction with theLookup
function if you want to bind to services in the Injector. Defaults to undefined. -
asInstance: boolean
: When true, the locator will callnew
on the service value when resolving it. Will return a new instance every call. Defaults to false.
-
-
resolve(dependencies: {} | [])
: Accepts an array or object of service keys and returns a matching collection of resolved services.- When using an array of service keys, the order of the returned services will match with the keys.
- When using an object of service keys, the name mapping is
{'name': 'ServiceKey'}
.
-
clearDependencyCache()
: Clears the resolution cache of all parameter bound services. When is this useful? If you are dynamically registering services (e.g. callinglocator.register()
inside a component or function) and want to force all future resolutions to forget the previous value of that registered service key, call this method to delete the previously cached services. The drawback to calling this function is that it forces certain service functions/classes to resolve as new object references (i.e.useEffect
anduseMemo
that were relying on consistent reference in some consumers will misbehave) so you need to be careful when you call it.
Usage
1class A {} 2class B {} 3 4function cHelper(num, str) { 5 return str + num; 6} 7 8class D { 9 constructor(KlassA, instanceB, num) { 10 this.instanceA = new KlassA(); 11 this.instanceB = instanceB; 12 this.num = num; 13 } 14} 15 16const locator = new ServiceLocator(); 17 18locator.register('A', A); 19locator.register('B', B, { asInstance: true }); 20locator.register('C', cHelper, { withParams: [123, 'Hi: '] }); 21locator.register('D', D, { 22 asInstance: true, 23 withParams: [Lookup('A'), Lookup('B'), 123], 24}); 25 26// Option 1: Array resolve 27 28const [KlassA, b, instanceC, d] = locator.resolve(['A', 'B', 'C', 'D']); 29 30// Option 2: Object resolve (equivalent) 31 32const { KlassA, b, instanceC, d } = locator.resolve({ 33 KlassA: 'A', 34 b: 'B', 35 instanceC: 'C', 36 d: 'D', 37});
Lookup()
Utility used in conjunction with the ServiceLocator.register
option
{withParams: []}
to indicate to the ServiceLocator it should look for a
service with that key during resolution.
Usage
1function foo(bar, iBar) { 2 console.assert(bar === 'Bar'); 3 console.assert(iBar instanceof Bar); 4} 5 6class Bar {} 7 8// In "withParams", 'Bar' will be a string bound to first argument of foo and 9// Lookup('Bar') will be an instance of Bar class bound to the second argument 10 11locator.register('Foo', foo, { withParams: ['Bar', Lookup('Bar')] }); 12 13locator.register('Bar', Bar, { isInstance: true }); 14 15const [resolvedFoo] = locator.resolve(['Foo']); 16resolvedFoo();
DecouplerProvider
React Context Provider. Wrap your components with this provider to enable the rest of the helper functions and components.
Supported Props
Supply one of the following props, but not both:
locator
: an instance of ServiceLocator (or API compatible object)services
: Vanilla JS object mapping service key names to services. e.g.{ServiceKey: class MyService {}}
Usage
1class MyService {}; 2 3const locatorInstance = new ServiceLocator(); 4 5locatorInstance.register('ServiceKey', MyService); 6 7function App() { 8 return ( 9 <DecouplerProvider locator={locatorInstance}> 10 <YourApp /> 11 </DecouplerProvider 12 ) 13}
Or with a "Services" mapping object.
1class MyService {}; 2 3const servicesMap = { 4 'ServiceKey': MyService 5}; 6 7function App() { 8 return ( 9 <DecouplerProvider services={servicesMap}> 10 <YourApp /> 11 </DecouplerProvider 12 ) 13}
useLocator()
Hook to return the internal ServiceLocator instance from context.
Usage
1function App() { 2 const locator = useLocator(); 3 const [A] = locator.resolve(['A']); 4 return <div />; 5}
useServices()
Hook to resolve and return the given dependencies.
Usage
1const SERVICES = ['A', 'B']; 2 3const ALT_SERVICES = { a: 'A', b: 'B' }; 4 5function App() { 6 const [A, B] = useServices(SERVICES); 7 const { a, b } = useServices(ALT_SERVICES); 8 9 return <div />; 10}
LocateServices
React Component for locating services and passing to a children render prop.
Usage
1function App() { 2 return ( 3 <LocateServices deps={['funcKey', 'ServiceClass', 'val']}> 4 {([func, ServiceClas, val]) => { 5 return <div />; 6 }} 7 </LocateServices> 8 ); 9}
withServices()
Higher-order Component for injecting services as props.
Usage
1// Array service resolution keys: 2function AppServiceArray({ services }) { 3 const [serviceA, serviceB] = services; 4 return <div />; 5} 6 7AppServiceArray.dependencies = ['AServiceKey', 'BServiceKey']; 8export const WrappedAppServiceArray = withServices(AppServiceArray); 9 10// Object service resolution keys: 11function AppServiceObj({ serviceA, serviceB }) { 12 return <div />; 13} 14 15AppServiceObj.dependencies = { 16 serviceA: 'AServiceKey', 17 serviceB: 'BServiceKey', 18}; 19 20export const WrappedAppServiceObj = withServices(AppServiceObj);
Contributing
Development of React Decoupler happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving React Decoupler.
TODO: contributing help
Code of Conduct
This project follows Test Double's code of conduct for all community interactions, including (but not limited to) one-on-one communications, public posts/comments, code reviews, pull requests, and GitHub issues. If violations occur, Test Double will take any action they deem appropriate for the infraction, up to and including blocking a user from the organization's repositories.
License
React Decoupler is MIT licensed.

No vulnerabilities found.

No security vulnerabilities found.