React hook to handle a shared service accross multiple components.
By service
we point an instance of a user-defined object using the new
operator.
A service may hold a state. That state will be shared to your components.
That means your components will be udpated if the service notify
it has been updated.
just add use-service
addon to your project:
npm i use-service
Then you can follow the example at https://github.com/toutpt/use-service/tree/master/src/App.js
you have two options:
// foo.js
function $foo() {}
// app.js
registerService('$foo', $foo);
or
// foo.js
function $foo() {}
$foo.id = '$foo';
// app.js
registerService($foo);
options.subscribe is set by default to true. In case performance are important you can set it to false if you know your component do not display the data of this service.
In the following example we build a service able to fetch with abort API. The state management is done by hand to show a real world example.
export class $bar {
public static $id = '$bar';
public isLoading: boolean = true;
public error: Error | null = null;
private controller: AbortController | null = null;
public data: any // up to you
constructor(private $apply:any) {
}
private reset() {
this.isLoading = true;
this.error = null;
this.$apply();
}
abort() {
if (this.controller) {
this.controller.abort();
}
}
fetchMe(){
this.reset();
this.controller = new AbortController();
return fetch('/api/bar', { signal: this.controller.signal }).then(data => {
this.data = data;
}).catch(e => {
if (e.name !== 'AbortError') {
this.error = e;
}
}).finally(() => {
this.isLoading = false;
this.$apply();
});
}
}
then, you can register and use it in your component
import React, {useEffect} from 'react';
import { useService } from 'use-service';
// this is one of the main difference
import { $foo as FooService } from './foo.service.ts';
export function MyBar() {
const $foo : FooService = useService('$foo');
useEffect(() => {
$foo.fetchMe();
return () => $foo.abort();
}, []);
if ($foo.error) {
return (
<div className="alert alert-danger">
<p>Could not load: {$foo.error.message}</p>
<button className="btn btn-default" onClick={() => $foo.fetchMe()}>re try<button>
</div>
);
}
if ($foo.isLoading) {
return "loading ... " // use your loading feedback component
}
return (
<div className="foo">
// display your $foo.data
</div>
);
}