How can we effectively divide NGXS state into manageable sections while still allowing them to interact seamlessly with one another?

Background of the inquiry:

I am in the process of developing a web assistant for the popular party game Mafia, and my objective is to store each individual game using NGXS. The GitLab repository for this project can be found here.

The game includes the following elements:

  1. List of players
  2. List of passed days (ranging from 0 to around 5)

You can view the Player model here.

Each day follows three phases: Night > Day > Vote

You can access the Day model here.

In order to manage this complex day structure and record all player actions, I encountered challenges due to the large number of required actions. As the GameState grew too big, I attempted to divide it into child states, but faced circular dependency issues.

Although I managed to address the problem by reviewing solutions like this one, utilizing CurrentDayState and PlayersState as child states seemed less than ideal because:

  • I could only directly access state, while selectors with intricate logic remained out of reach.
  • I was storing only the current day and vote, pushing them into the GameState's days array, which would complicate going back to previous days if necessary.

I feel that my approach may be cumbersome, and despite efforts, I haven't been able to devise a better solution. While storing all actions in the root GameState is an option, this would lead to an excessively lengthy .ts file, which doesn't seem optimal.


The query:

Is there a method to modularize NGXS state, allowing each component to access selectors from other parts without encountering circular dependencies? (I'm aware sub-states in NGXS are meant to be autonomous and should not exert control over one another).

Answer №1

To tackle this issue from a different angle, you can treat each store as a database table and every new state created as a new record/row in that table. By normalizing your models, you could establish a player store and a day store. Instead of updating current states, continually insert new states to preserve the complete history of days and player states for any given day. Players may require an attribute to denote the timing of their state (such as a foreign key linked to the corresponding day state).

If necessary, selectors can be formulated to access the current state or retrieve a particular state based on a specific time in the game's history. The ngxs documentation on selectors suggests using Meta-Selectors to combine unrelated states. This approach allows you to merge the relevant player and day states when needed.

Edit 1: By storing normalized data and snapshots, you can address the concerns related to maintaining comprehensive game histories and multiple management states effectively.

Regarding the dilemma of managing different states without creating circular dependencies, it is advisable to refrain from importing two files into each other. Here are two strategies to handle this situation:

  1. Determine where the states need to be merged - designate one as the basis for the primary business object - and import the files unidirectionally into that state only.

  2. If none of the states serve as the foundation for the primary business object, introduce a third state to fulfill this role and import the other states into it.

Subsequently, utilize the Meta-Selector offered by NGXS to integrate the pertinent states at that juncture.

eg.

export class DaysState{
  @Selector([DaysState, PlayersState])
  static getGameState(days, players) {
    return [...days, ...players];
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What factors should I consider when determining whether to include @types/* in the `dependencies` or `devDependencies` section?

Currently using TypeScript 2 in my project, I am looking to incorporate a JavaScript library along with its typings. While I know I can easily install the types using npm install @types/some-library, I am uncertain whether to use --save or --save-dev. The ...

What are the steps for implementing the ReactElement type?

After researching the combination of Typescript with React, I stumbled upon the type "ReactElement" and its definition is as follows: interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor< ...

Tips for sending asynchronous data to Google-Charts-Angular

I am currently working on generating a chart using data obtained from an API call. To achieve this, I am utilizing the google-charts-angular package within my HTML: <google-chart [title]="title" [type]="type" [data]="data" ...

An issue has occurred: Angular2 is reporting that Observable_1.Observable.defer is not recognized as a

Recently, I made the transition of my Angular app from version 4 to 6. Here is a peek at my package details: Package.json file: { "version": "0.10.50", "license": "MIT", "angular-cli": {}, ... Upon running npm run start, an error popped up in th ...

Ionic: Fixed button located at the bottom of a specific ion-slide

I've been creating a series of slides with questions, and the final slide serves as a summary of the previously answered questions. I want to ensure that the submit button is always visible at the bottom of this last slide. However, I've encounte ...

Updated the application to Angular 6 but encountered errors when attempting to run npm run build --prod. However, running the command npm run build --env=prod was executed without any issues

Running the command npm run build -- --prod results in the following error messages: 'PropertyName1' is private and can only be accessed within the 'AppComponent' class 'PropertyName2' does not exist in type 'AppCompo ...

Creating multiple CRUD components in Angular 2: which is better, using routing or a parent component

My application consists of multiple sections that act as independent CRUD components. There are two approaches that I am aware of: Utilize a parent component with ngIfs to manage the view/edit/add operations of child components Implement subrouting wit ...

What is the best way to dynamically disable choices in mat-select depending on the option chosen?

I was recently working on a project that involved using mat-select elements. In this project, I encountered a specific requirement where I needed to achieve the following two tasks: When the 'all' option is selected in either of the mat-select e ...

The specified key is not found in the 'StoreOptions<State>' type of Vuex 4, in combination with Vue 3 and Typescript

I'm navigating the process of setting up a Vuex 4 Store with Typescript and Vue3, despite having limited experience with typescript. Following the Vuex Tutorial for the initial setup, I almost entirely copy-pasted the code. The only difference is tha ...

When you initiate an Angular 8 project on a Windows 10 system, it generates extra files

Just a few days ago, I made the decision to transition from using an Ubuntu virtual machine for my Angular and development projects to working on Windows. The reason behind this shift was that I already had Node installed along with Git and other necessary ...

Angular 2 does not recognize the element 'child-selector' as valid

In my quest to establish communication from a child component to a parent component based on certain actions, I have incorporated the use of both EventEmitter and Output libraries. Let me walk you through what I have achieved thus far. Firstly, let's ...

Inject parameter into MdDialog in Angular Material 2

I am currently utilizing Angular Material 2 and I have a requirement to display a dialog window using MdDialog that contains information about a user stored in Firebase. @Injectable() export class TweetService { dialogRef: MdDialogRef<TweetDialogCom ...

TypeScript is encountering difficulty locating a node module containing the index.d.ts file

When attempting to utilize EventEmitter3, I am using the following syntax: import EventEmitter from 'eventemitter3' The module is installed in the ./node_modules directory. It contains an index.d.ts file, so it should be recognized by Typescrip ...

An unfamiliar data type is provided as a number but is treated as a string that behaves like a number

Here is the code snippet in question: let myVar = unknown; myVar = 5; console.log((myVar as string) + 5); Upon running this code, it surprisingly outputs 10 instead of what I expected to be 55. Can someone help me understand why? ...

What is the proper way to utilize a class with conditional export within the Angular app.module?

This query marks the initiation of the narrative for those seeking a deeper understanding. In an attempt to incorporate this class into app.module: import { Injectable } from '@angular/core'; import { KeycloakService } from 'keycloak-angul ...

Insert a fresh element above the existing elements fetched from the API

My application consists of two components: add-product.component and show-list.component. The show-list component displays a list obtained from the list-product API, which requires 3 parameters in the body (user_id, session_key, page). The third parameter ...

Encountering a Typescript error while attempting to remove an event that has a FormEvent type

Struggling to remove an event listener in Typescript due to a mismatch between the expected type EventListenerOrEventListenerObject and the actual type of FormEvent: private saveHighScore (event: React.FormEvent<HTMLInputElement>) { This is how I t ...

When an Angular2 application is deployed on a server running NginX, child components fail to load

After deploying my Angular2 app on a server as a Docker image and serving it with NginX, I encountered an unexpected issue. When I ran the webpack-dev-server locally to verify if the build was successful, everything looked fine https://i.sstatic.net/wQQ7 ...

Steps to resolve the issue of 'type is not assignable to any' while working with a member

I'm facing an issue with a code snippet like the one below: interface IFoo { bar: string; baz: number; } function f(foo: IFoo, name: 'bar' | 'baz', val: any) { foo[name] = val; // <<< error: Type 'any' i ...

Develop a user interface designed specifically for a subset of JSX.Elements or ReactElement

For better organization, I decided to create an interface called IconInterface to group all my icons: import { IconProps, CaretProps, CheckboxProps } from "./IconProps"; interface IconInterface { (props: IconProps | CaretProps | CheckboxProp ...