How to set React states outside React components? - node.js

I'm creating a browser multiplayer card game with React and Socket.IO. On the client, I have a clientController class which is responsible for connecting to the server, and communicating with it using Socket.IO. This is also where I define listeners for game events (like "start_game" or "new_round"). All these events are handled by listeners in a gameService file (which exports a single instance of a GameService class per client).
I'm using TypeScript and styled-components, but I don't think it is relevant for this question.
I have a GameRoom component (mainly for rendering, not much logic), but my plan was to manipulate most or all game state inside my GameService instance. This obviously means that my GameService instance would need to have access to the React Context I set up (for accessing and changing state everywhere), which it can't have because it is not a React component.
I tried defining variables in my GameService class for later declaration inside gameContext file, like so:
//inside GameService class
class GameService {
setGame;
...
}
export default new GameService();
//inside gameContext file
import gameService from "./gameService"
...
const [game, setGame] = useState<Game>();
gameService.setGame = setGame;
Even doing it this way, which avoids calling useContext() inside a file which has nothing to do with React components, it doesn't work.
For example, inside my "start_game" event listener (inside my GameService instance), I call this.setGame(new Game()) and it gives me a "this.setGame is not a function" error.
I think this has more issues than just the state setters, as the "game" state itself would probably just be converted to a normal variable.
Is there a way to effectively use state inside a non React component? Or should I turn my GameService class into a component that just handles logic? If so, where should I place it in my component hierarchy?

Related

Using Typescript Declaration (.d.ts) Files to Expose Microservice API

I'm fairly new to Typescript.
I have 2 microservices, let's call them ManagerMs and HandlerMs.
They communicate through RabbitMq.
Each of their public methods, becomes a queue on Rabbit when service starts.
The ManagerMs need to preform an RPC call on a function, called 'handle' that belongs to HandlerMs.
Ideally, I want ManagerMs to be able to import just the declarations of HandlerMs so that it can do something like (inside the ManagerMs class):
import HandlerMsApi from '<path-to-declaration-file?>'
...
class ManagerMs {
...
this.rpcClient.call(HandlerMsApi.handle.name) // instead of: this.rpcClient.call('HandlerMsApi.handle')
The point is that a certain service will have access to the declaration of another service and not the implementation.
Currently, both services can't import each other because of the way the project is structured.
So I thought of creating a shared library which will hold just the declaration files of the different modules, but that mean that .d.ts files aren't located next to their corresponding implementation files (.ts).
Questions are:
Is it a good idea?
How can I achieve such behaviour?
Currently, when I tried to do so I have the following .d.ts file (in a different folder than the implementation):
declare class HandlerMsApi {
handle(req: string): Promise<any>;
}
export = HandlerMsApi;
But when I try to compile (tsc) my code I get the following error:
"....handler.d.ts' is not a module"
Any help?

Event Emitter gets emmited multiple times , Nest JS

I am new to Event Emitters and tried implementing them in my Nest JS project, the problem is that the same event gets triggered multiple times (6 times to be exact) , any reason as to why and how to resolve this ?
This is the code snippet in the service.ts file to emit the event.
this.eventEmitter.emit('company.created', companyCreatedHistory);
This is my Event , in the listener.ts file
#Injectable()
export class EntityCreatedListener {
constructor(private readonly historyService: HistoriesService) {}
#OnEvent('company.created')
handleCompanyCreatedEvent(eventObject: typeof eventEmitterObject) {
console.log('Hi')
this.createAuditLog(eventObject, 'company.created');
}
Referred from : https://github.com/nestjs/nest/tree/master/sample/30-event-emitter
https://www.npmjs.com/package/#nestjs/event-emitter
So looking at your code I am guessing the problem is because you have different instances of the EntityCreatedListener flying around in your application. You probably have instantiated it 6 times that's why the event is firing 6 times.
All these instances of the EntityCreatedListener will be listening for the company.created event and will fire once this method call this.eventEmitter.emit('company.created', companyCreatedHistory); triggers the event.
THE FIX WOULD BE: Check the modules in your application and make sure the EntityCreatedListener is not imported in multiple places in the provider imports on this section of the module code #Module({imports: [],controllers: [],providers: []}). Keep only one instance of the EntityCreatedListener (thus it should be imported in only one module and shared to other modules by exporting it if you want to use in multiple places)

Is there a recommended approach to referencing an instantiated class object in React / Nextjs?

Let's say I have a Node.js library that I need to instantiate in my React app.
const awesomeLibrary = new AwesomeLibrary('someID', options);
awesomeLibrary allows me to do things like awesomeLibrary.configure(), awesomeLibrary.specifyCustomProperty() and awesomeLibrary.fireEvent().
I would like to instantiate it once and then call those methods from the instantiated reference (awesomeLibrary) around my application. What is the best way to that?
Would I instantiate it in top-level entry component (e.g. _app.tsx), export it, import it into other components and call the methods? In order to export it, I'd have to instantiate it outside the component, instead of in the lifecycle of the component. Is that a bad practice?
Alternatively, could I relegate the instantiation to a hook? Say useAwesomeLibrary() in which I instantiate and export the methods? But wouldn't doing that create an instance every time the hook is called from different components?
I'm really not sure how to handle this.
Instantiating class before exporting it is a fairly common practice and one of the possible implementations of singleton in JavaScript/React. For example, you can use popular i18next library in this way:
services/i18n.js:
import i18n from 'i18next';
i18n.init({
// pass translations and other config
});
export default i18n;
_app.js:
import i18n from 'services/i18n';
// snip
<I18NextProvider i18n={i18n} /> // consume already instantiated i18n object
The difference between this and instantiating object in a hook is WHEN it will be instantiated. When you do this in hook, it will instantiate every time you mount component with this hook. When in separate file (module), then it will be once per page load.
Is that a bad practice? I don't know, it depends on how you'd use it. There is nothing bad in doing this per se, and even it has its advantages (in the example above we delegated responsibility of creating and configuring i18n service from _app.js, which is very good from Single Responsibility Principle perspective). But keep in mind that codebase bloated with singletons will probably become hard to maintain.

Property observed not exist on type EventEmitter [duplicate]

I've read questions like Access EventEmitter Service inside of CustomHttp
where the user uses EventEmitter in his service, but he was suggested in this comment
not to use it and to use instead Observables directly in his services.
I also read this
question
where the solution suggests to pass the EventEmitter to the child and subscribe to it.
My question then is: Should I, or should I not subscribe manually to an EventEmitter? How should I use it?
TL;DR:
No, don't subscribe manually to them, don't use them in services. Use them as is shown in the documentation only to emit events in components. Don't defeat angular's abstraction.
Answer:
No, you should not subscribe manually to it.
EventEmitter is an angular2 abstraction and its only purpose is to emit events in components. Quoting a comment from Rob Wormald
[...] EventEmitter is really an Angular abstraction, and should be used pretty much only for emitting custom Events in components. Otherwise, just use Rx as if it was any other library.
This is stated really clear in EventEmitter's documentation.
Use by directives and components to emit custom Events.
What's wrong about using it?
Angular2 will never guarantee us that EventEmitter will continue being an Observable. So that means refactoring our code if it changes. The only API we must access is its emit() method. We should never subscribe manually to an EventEmitter.
All the stated above is more clear in this Ward Bell's comment (recommended to read the article, and the answer to that comment). Quoting for reference
Do NOT count on EventEmitter continuing to be an Observable!
Do NOT count on those Observable operators being there in the future!
These will be deprecated soon and probably removed before release.
Use EventEmitter only for event binding between a child and parent component. Do not subscribe to it. Do not call any of those methods. Only call eve.emit()
His comment is in line with Rob's comment long time ago.
So, how to use it properly?
Simply use it to emit events from your component. Take a look a the following example.
#Component({
selector : 'child',
template : `
<button (click)="sendNotification()">Notify my parent!</button>
`
})
class Child {
#Output() notifyParent: EventEmitter<any> = new EventEmitter();
sendNotification() {
this.notifyParent.emit('Some value to send to the parent');
}
}
#Component({
selector : 'parent',
template : `
<child (notifyParent)="getNotification($event)"></child>
`
})
class Parent {
getNotification(evt) {
// Do something with the notification (evt) sent by the child!
}
}
How not to use it?
class MyService {
#Output() myServiceEvent : EventEmitter<any> = new EventEmitter();
}
Stop right there... you're already wrong...
Hopefully these two simple examples will clarify EventEmitter's proper usage.
Yes, go ahead and use it.
EventEmitter is a public, documented type in the final Angular Core API. Whether or not it is based on Observable is irrelevant; if its documented emit and subscribe methods suit what you need, then go ahead and use it.
As also stated in the docs:
Uses Rx.Observable but provides an adapter to make it work as specified here: https://github.com/jhusain/observable-spec
Once a reference implementation of the spec is available, switch to it.
So they wanted an Observable like object that behaved in a certain way, they implemented it, and made it public. If it were merely an internal Angular abstraction that shouldn't be used, they wouldn't have made it public.
There are plenty of times when it's useful to have an emitter which sends events of a specific type. If that's your use case, go for it. If/when a reference implementation of the spec they link to is available, it should be a drop-in replacement, just as with any other polyfill.
Just be sure that the generator you pass to the subscribe() function follows the linked spec. The returned object is guaranteed to have an unsubscribe method which should be called to free any references to the generator (this is currently an RxJs Subscription object but that is indeed an implementation detail which should not be depended on).
export class MyServiceEvent {
message: string;
eventId: number;
}
export class MyService {
public onChange: EventEmitter<MyServiceEvent> = new EventEmitter<MyServiceEvent>();
public doSomething(message: string) {
// do something, then...
this.onChange.emit({message: message, eventId: 42});
}
}
export class MyConsumer {
private _serviceSubscription;
constructor(private service: MyService) {
this._serviceSubscription = this.service.onChange.subscribe({
next: (event: MyServiceEvent) => {
console.log(`Received message #${event.eventId}: ${event.message}`);
}
})
}
public consume() {
// do some stuff, then later...
this.cleanup();
}
private cleanup() {
this._serviceSubscription.unsubscribe();
}
}
All of the strongly-worded doom and gloom predictions seem to stem from a single Stack Overflow comment from a single developer on a pre-release version of Angular 2.
When you want to have cross component interaction, then you need to know what are #Input , #Output , EventEmitter and Subjects.
If the relation between components is parent- child or vice versa we use #input & #output with event emitter..
#output emits an event and you need to emit using event emitter.
If it's not parent child relationship.. then you have to use subjects or through a common service.
When you want component interaction, then you need to know what are #Input , #Output , EventEmitter and Subjects.
If the relation between components is parent- child or vice versa we use #input & #output with event emitter..
#output emits an event and you need to emit using event emitter.
If it's not parent child relationship.. then you have to use subjects or through a common service
There is no: nono and no: yesyes.
The truth is in the middle
And no reasons to be scared because of the next version of Angular.
From a logical point of view, if You have a Component and You want to inform other components that something happens, an event should be fired and this can be done in whatever way You (developer) think it should be done. I don't see the reason why to not use it and i don't see the reason why to use it at all costs. Also the EventEmitter name suggests to me an event happening. I usually use it for important events happening in the Component. I create the Service but create the Service file inside the Component Folder. So my Service file becomes a sort of Event Manager or an Event Interface, so I can figure out at glance to which event I can subscribe on the current component.
I know..Maybe I'm a bit an old fashioned developer.
But this is not a part of Event Driven development pattern, this is part of the software architecture decisions of Your particular project.
Some other guys may think that use Observables directly is cool. In that case go ahead with Observables directly.
You're not a serial killer doing this. Unless you're a psychopath developer, So far the Program works, do it.
From a pure implementation perspective, since emit and subscribe are part of the public interface of EventEmitter, they can be used for implementation.
For angular there was no compulsion to inherit from behaviour if it didn't want it to, Behaviour could have been a private member in EventEmitter class, something like,
public class EventEmitter{
private _behaviour=new Subject<void>();
private _behaviour$=this._behaviour.asObservable();
......
public emit(){
_behaviour.emit();
}
....
}
If it inherits from Behvaiour but doesn't behave like one, then it is violation of liskov's susbstitution principle.

posibilty to get service whithout moduleRef.get

I Can take instance of service using this example:
export class GetEntityDomainService {
constructor(private readonly moduleRef: ModuleRef) { }
getEntity(): void {
const myObject = this.moduleRef.get(MyClassName);
}
}
but how could I invoke a service instance outside of a class object
where I don't have a handle to moduleRef:
here is example from angular:
const injector = ReflectiveInjector.resolveAndCreate(providers);
const widgets: WidgetService = injector.get(WidgetService);
https://kevinphelps.me/blog/2017-01-17-using-angular-dependency-injection-in-node
whether is it possible to download an instance of the service without needing a moduleRef?
Thanks
Piotr
There are several reasons
sometimes I need to inject a service in a function, then I have to pass moduleRef to this function to make it possible
The second example is Command and Events -
for example I create such an action, then I pass parameters to send to handler,:
this.dispach(new Command (parameters ...))
Currently my actions know from which module they are triggered and in which module their handler is - so I won't send actions between wrong areas of the system (layers, dependencies). I check all this when executing
this.dispach (new Command ())
now I have to write such a dispatch as follows:
this.dispach (new Command (this.moduleRef, ... other parameters))
could I create a moduleRef from the code?, it would facilitate the process of creating an action and eliminate the need to pass moduleRef
but I don't know if it will help me
as I analyzed DI nest, it works differently from angular
The service knows where it was declared and when it injects something in it, it uses the tokens declared in this module
I suppose I will only be able to inject general scope

Resources