The Angular Ngrx store function, store.select('selector name'), should ideally provide a list of Books but instead, it is returning a non-iterable list

Can someone help me troubleshoot this issue? It's my first time using state management.

The result I get when trying to fetch it from the state is not what I expected.

https://i.sstatic.net/QD7o5.png

Here is the reducer:

import {createReducer, on} from '@ngrx/store';
import {getBooksSuccess} from './book-list.actions';
import {Book} from '../../models/book.model';

const initialState: Array<Book> = [];

export interface BookState {
  bookList: Book[];
}

export const bookReducer = createReducer(
  initialState,
  on(getBooksSuccess, (state, action) => {
    return {
      ...state,
      books: action,
    };
  })
);

export function BookReducer(state, action) {
  return bookReducer(state, action);
}

Next is the action:

import {createAction} from '@ngrx/store';
import {Book} from '../../models/book.model';

export const FETCH_BOOKS_START = '[Books] books start';
export const FETCH_BOOKS_SUCCESS = '[Books] books Success';

export const getBooks = createAction(
  FETCH_BOOKS_START
);

export const getBooksSuccess = createAction(
  FETCH_BOOKS_SUCCESS,
  (books: ReadonlyArray<Book>) => books
);

Then we have the effect:

fetchBooks$ = createEffect(() => {
  return this.actions$.pipe(
    ofType(getBooks),
    mergeMap(() => {
      return this.bookService.getBooks().pipe(
        map((bookList) => {
          this.books = this.bookService.mapToBooksModel(bookList);
          return getBooksSuccess(this.books);
        })
      );
    })
  );
});

The service class looks like this:

@Injectable({
  providedIn: 'root',
})
export class BookService {
  responseData: BookResponseData[];
  bookList: Book[];

  constructor(private http: HttpClient) {
  }

  getBooks(): Observable<BookResponseData[]> {
    return this.http.get<BookResponseData[]>(
      `./assets/books.json`,
    );
  }

  mapToBooksModel(books: BookResponseData[]) {
      this.bookList = books;
      return this.bookList;
    }
}

Next up is the model class definition:

export interface Book {
  id: string;
  name: string;
  author: string;
}

export interface BookResponseData {
  id: string;
  name: string;
  author: string;
}

Here's the selector:

import {BookState} from './book-list.reducer';
import {Book} from '../../models/book.model';
import {createSelector} from '@ngrx/store';

export const BOOK_STATE_NAME = 'bookList';

export const bookSelector = createSelector(
  (state: BookState) => state.bookList,
  (books: Array<Book>) => books
);

And finally, the BookListComponent:

export class BookListComponent implements OnInit {

  book$: Observable<Book[]>;
  books: Book[];

  constructor(private store: Store<BookState>) {}

  ngOnInit(): void {
   this.store.dispatch(getBooks());
   this.store.select(bookSelector).pipe().subscribe( books => {
      this.books = books;
      console.log(this.books);
    });
  }

Answer №1

It seems like there may be an error in this section. Instead of assigning an action object to the books property, you should only need its payload. You can either assign it like this books: action.books or simply destructure the action object.

export const bookReducer = createReducer(
  initialState,
  on(getBooksSuccess, (state, { books }) => {
    return {
      ...state,
      books
    };
  })
);

UPDATE: Upon reviewing your question again, I have identified several potential issues that may require attention.

// BookService
getBooks(): Observable<BookResponseData[]> {
  return this.http.get<BookResponseData[]>(`./assets/books.json`)
    .pipe(map(books) => {
      this.bookList = books.map(b => {...b}); //create a copy, otherwise some weird reference issues might arise
      return this.bookList;
    });
}
// 'mapToBooksModel' function is no longer necessary

Your action definition appears unusual. Consider trying this format instead.

// books action
export const getBooksSuccess = createAction(
  FETCH_BOOKS_SUCCESS,
  props<{books: ReadonlyArray<Book>}>() // defines payload, making books accessible
);

Your effect implementation could benefit from some improvements as well. Storing the service result in a class property (this.books) can lead to issues where state changes occur without using reducers when not in strict mode. Additionally, what does the getBooksSuccess function do?

fectchBooks$ = createEffect(() => 
  this.actions$.pipe(
    ofType(getBooks),
    concatMap(() => this.bookService.getBooks())
    map((bookList) => getBooksSuccess(bookList))
  )
);

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

Tips for importing a file with a dynamic path in Angular 6

I'm facing an issue where I need to import a file from a path specified in a variable's value. For example, it looks like this: let test = require(configurationUrl); Here, configurationUrl represents a path such as '../../assets/app.conf.j ...

Regex for US zip code with an optional format

Searching for a regular expression to validate US zip codes. I have come across multiple examples, but none of them cater to the scenario where the zip code is optional. The input field I am working on does not require a zip code, so it should accept a 5 ...

I'm having difficulty updating the Angular CLI version

I am currently running Angular CLI version 7.1.4 and I have been attempting to update to the latest version. However, each time I try to update, I encounter a multitude of errors. I have attempted using the command ng update @angular/core @angular/cli b ...

Need at least one of two methods, or both, in an abstract class

Consider the following scenario: export abstract class AbstractButton { // Must always provide this method abstract someRequiredMethod(): void; // The successor must implement one of these (or both) abstract setInnerText?(): void; abst ...

Typescript - Defining string value interfaces

I have a property that can only be assigned one of four specific strings. Currently, I am using a simple | to define these options. However, I want to reuse these types in other parts of my code. How can I create an interface that includes just these 4 va ...

difficulties managing asynchronous operations and observables within Angular

For my project, I am required to perform data calculations based on specific dates (month and year). Once I have these dates stored in the array "this.etiquetasEjeX", I iterate through them to calculate the data within my component like so: ngOnInit() { th ...

What is the process for configuring vue.config.js with typescript?

Just starting out with typescript and running into an issue while configuring vue.config.js const webpack = require("webpack"); module.exports = { plugins: [ new webpack.DefinePlugin({ __VUE_I18N_FULL_INSTALL__: true, __ ...

Ordering Server Responses with Angular's httpClientAngular's httpClient allows

Utilizing theHTTPClient module to automatically map data in a service, which then uses http.get to connect to a remote API. I subscribe to this service in a component that calls it multiple times within a loop. for (let i of this.symbols) { this.serv ...

Extending a generic typed class in Typescript allows for the creation of

I am interested in extending the following class: import 'reflect-metadata'; import { IRepository, IFireOrmQueryLine, IOrderByParams, IEntity } from './types'; import { AbstractFirestoreRepository } from './AbstractFirestoreReposit ...

Sign in to Azure for an Angular application within the iFrame of another Angular application

I have two separate Angular apps, both enabled with Azure AD login. When accessed separately in the tab, they function properly - redirecting and obtaining the token without issue. Now, I aim to make Angular 1 the Parent and Angular 2 the child within a s ...

What is the best way to send props (or a comparable value) to the {children} component within RootLayout when using the app router in Next.js

As I work on updating an e-commerce website's shopping cart to Next.js 13+, I refer back to an old tutorial for guidance. In the previous version of the tutorial, the _app.ts file utilized page routing with the following code snippet: return ( < ...

Utilize the canActivate method to decide when to display a specific hyperlink

I'm facing a challenge with my AuthGuard implementation using CanActivate to decide whether or not to display a link. Despite my efforts, I am unable to properly check the canActivate property of the Route. Here is the function I created: TypeScript ...

Enhancing Angular functionality with the addition of values to an array in a separate component

I need help with adding a value to an array in the 2nd component when a button in the 1st component is clicked. I am working on Angular 4. How can I achieve this? @Component({ selector: 'app-sibling', template: ` {{message}} <butt ...

A guide on utilizing the useEffect hook to dynamically update a button icon when hovering over it in a React application

Is it possible to change the icon on a button when hovering using useEffect? <Button style={{ backgroundColor: "transparent" }} type="primary" icon={<img src={plusCart} />} onCl ...

Issue with expanding TileLayer using TypeScript 2.0 and Angular 2: Unable to implement Leaflet Leaflet

Trying to incorporate the Microsoft Map API into my app has been a challenge, especially when I encounter errors like the one below when calling L.tileLayer.extend: Here is the snippet of my code: import { Component, OnInit } from '@angular/core&apo ...

Searching for a string within a JSON object in Angular: step-by-step guide

JSON Data Example { "rootData": { "test1": { "testData0": "Previous information", "testData1": "Earlier Information" }, "test2": { "testData0": ...

What are the steps to ensure a successful deeplink integration on iOS with Ionic?

Recently, I was working on a hybrid mobile app for Android/iOS using Nuxt 3, TypeScript, and Ionic. The main purpose of the app is to serve as an online store. One important feature involves redirecting users to the epay Halyk website during the payment pr ...

Utilizing HttpHeaders Link for pagination within an Angular application

Upon receiving a response from api.github, there is a response header containing the Link. How can this be utilized? It returns a string similar to: <https://api.github.com/user/repos?per_page=10&page=2>; rel="next", <https://api.github.com/ ...

Guide on transforming observable<User> to Observable<boolean> in Angular 4

As a newcomer in Angular and Mean Stack, I need help implementing the canActivate() method to restrict admin routes. In my service file, the checkAdmin method returns an observable of type "User", but the canActivate method's return type is an observa ...

I'm curious about the type I can set for the first parameter of setState in TypeScript. Is there a way to pass a dynamically generated state object to setState?

When trying to pass a newState object to setState and add some additional properties under certain conditions, I encountered a type error: I attempted to define the new State as Pick<ItemListState, keyof ItemListState> but received a type error ...