I needed a React tool that would allow me to separate the view layer with other application layers. I wanted to be able to create services that are completely separated, that know nothing about the view. When I succeeded, there was a problem with injecting these services into individual components (at any application level). For this purpose a react-hot-wire was created. It allows to define what services a given component needs, and enables convenient injection. Additionally, the component can react to changes in services by plugging its own listener into the service cycle.
$ npm install --save react-hot-wire$ npm install --save hot-wireFirst we should define the service(s) to be solved by the hot-wire module.
Example service:
// services/language.service.jsexportdefaultclassLanguageService{_currentLanguage='en';currentLanguage(){returnthis._currentLanguage;}}Then by using hot-wire we create the instances of the services and inject dependencies into the appropriate places (I suggest you see the hot-wire tests). Then we pass the solved services to Provider:
// index.jsimportReactDOMfrom'react-dom';importHotWirefrom'hot-wire';import{Provider}from'react-hot-wire';importLanguageServicefrom'services/language.service';constcontainer=newHotWire().wire({services: {// this is example usage, we can store definitions whenever we wantlanguageService: {class: LanguageService,public: true,},// other definitions of services here...},});container.then(services=>{ReactDOM.render(<Providerservices={services}>{/* application code here */}</Provider>,document.getElementById('app'));});Now you would like to inject into a component, at any level, an instance of a selected service. With help comes the wire function, which in arguments accepts the component that is to have the service injected, and the array of services to be set in props:
// components/language.component.jsimportReactfrom'react';import{wire}from'react-hot-wire';functionLanguage({ languageService }){returnlanguageService.currentLanguage();}exportdefaultwire(['languageService'],Language);or simply:
importReactfrom'react';import{Wire}from'react-hot-wire';importLanguagefrom'components/language.component';exportfunctionApp(){return(<Wireservices={['languageService']}render={({ languageService })=><Languagelang={languageService.currentLanguage()}/>}/>);}Now we have injected the selected service into the component, and we can take full advantage of its capabilities. As the service was injected by props, we have the possibility of convenient component testing, substitution of this service, etc.
// hoc/language.hoc.jsimportReact,{useEffect}from'react';import{wire}from'react-hot-wire';exportdefaultComponent=>wire(['languageService'],functionLanguageHOC({ languageService, ...props}){useEffect(()=>{returnlanguageService.addChangeListener(()=>{}/* do something... */);});return<Componentlang={languageService.currentLanguage()}{...props}/>;});// components/language.component.jsimportLanguageHOCfrom'hoc/language.hoc';exportfunctionLanguage({ lang }){returnlang;}exportdefaultLanguageHOC(Language);- and small modification in the example service (extends for a
Service)
- export default class LanguageService {+ import { Service } from 'react-hot-wire';++ export default class LanguageService extends Service {- property
services: Object<string, Object>- services after a resolved container
property
services: string[]property
render: (services: Object<string, Object>): Component
wire(Component: Component, dependencies: string[]): Component
addChangeListener(changeListener: Function): FunctionrunChangeListeners(): void