Форма моего состояния выглядит следующим образом
@State<ProductDataModel>({
name: 'productDataState',
defaults:{
categories: [],
currentProduct: {
productId : '',
name : '',
description : '',
instructionKey : '',
models : []
}
}
})
Я создал @Selector()
для возврата свойства currentProduct
, которое выглядит следующим образом.
@Selector()
static fetchCurrentProduct(state:ProductDataModel){
console.log(state.currentProduct);
return state.currentProduct;
}
The console.log()
здесь возвращает 2 currentProduct
объектов, когда загружается мой компонент, который внутри моего компонента использует @Select(state.data) VariableName$
способ вызова данных. Когда console.log()
в переменной @Select()
внутри компонента в хуке ngOnInit()
количество возвращаемых им ответов увеличивается на один каждый экземпляр.
Я создал @Action()
для установки currentProduct
свойство путем проверки внутри состояния для продукта, если оно существует, оно подключает, оно исправляет свойство currentProduct
. Если он не существует, он отправляет запрос API в мое приложение Nest JS, добавляет его в соответствующую категорию и устанавливает его в качестве текущего продукта.
export class FetchProduct{
static readonly type='[Product Data] Fetch Product';
constructor(public category: string, public product: string){console.log(product);}
}
@Action(FetchProduct)
public fetchProduct(ctx:StateContext<ProductDataModel>, {category, product}: FetchProduct){
const currentState = ctx.getState();//gets current state
//fetches the category from the state
const categoryMatch = currentState.categories.find(cat => cat.productCategoryId === category);
//search to see if the product already exists
const productMatch = categoryMatch.products.find(p => p.productId === product);
//if the product exists set it as the current product
if(productMatch){ ctx.patchState({currentProduct: productMatch}); }
//if the product doesn't exist fetch it from the backend app
else{
//fetchProductData() makes api request and returns an Observable
return this.dataService.fetchProductData(category, product).pipe(tap(result =>{
//empty variable to store mutated category data
const newCategoryList: ProductCategoryModel[] = [];
//clone of original category data
const categoryList: ProductCategoryModel[] = [...currentState.categories];
//creates an object out of the returned product
const newProduct: ProductDataItem ={
productId: result.productId,
name: result.name,
description: result.description,
instructionKey: result.instructionKey,
models: result.models
};
//create updated category data
categoryList.forEach(a=>{
//adds product to respective category and pushes to empty variable
if(a.productCategoryId === category){
const newItem: ProductCategoryModel = {
productCategoryId: a.productCategoryId,
name: a.name,
description: a.description,
products: [...a.products, newProduct]
};
newCategoryList.push(newItem);
}
//pushes categories to variable that don't need mutating
else{ newCategoryList.push(a); }
});
//updates the state
ctx.patchState({categories: newCategoryList, currentProduct: newProduct});
}));
}
}
Я заметил, что когда я нажимаю на продукт, который уже был загружен в состояние, вызывающее этот фрагмент
if(productMatch){ ctx.patchState({currentProduct: productMatch}); }
Все работает точно так, как ожидалось.
Кажется, возникает проблема, когда запрос API добавляется в уравнение. Однако при прохождении каждой части моего действия все возвращается, как и ожидалось. Регистрация свойств внутри конструктора моего FetchProduct
класса показывает только один вызов, инициируемый за клик. Функция в моем служебном файле, которая делает вызов API, выглядит следующим образом.
public fetchProductData(category: string, product: string):Observable<ProductDataItem> /*ProductDataItem*/{
const path = `${this.URL}/get-product/find-product?cattegory=${category}&product=${product}`;
const data: Observable<ProductDataItem> = this.httpClient.get(path) as Observable<ProductDataItem>;
data.subscribe(a=> console.log(a));
return data;
}
console.log()
здесь также возвращает только один ответ на клик. То, как я вызываю свойство currentProduct
в моем компоненте, выглядит следующим образом.
@Select(ProductDataState.fetchCurrentProduct) Product$: Observable<ProductDataItem>;
Затем я делаю это внутри OnInit
hook
this.Product$.subscribe(a=> console.log(a));
Как я уже говорил ранее это также показывает несколько экземпляров, за исключением того, что число возвращаемых значений увеличивается с каждым кликом.
Я дошел до того, что изменил свое действие следующим образом
//set the currentProduct back to an empty object
ctx.patchState({currentProduct: {
productId: '',
name: '',
description: '',
instructionKey: '',
models: []
}});
//adds product to category
ctx.patchState({categories: [...newCategoryList]});
//sets currentProduct from the object now saved inside the state
ctx.patchState({
currentProduct: ctx.getState().categories.find(a=>a.productCategoryId === category)
.products.find(a=> a.productId === product)
});
Я пытался это увидеть Если бы что-то изменилось, обновив его так же, как это происходит, когда продукт уже существует, но я все равно получаю те же результаты. Кто-нибудь может увидеть, что я делаю неправильно, чтобы создать такое поведение, или кто-нибудь знает что-то под капотом с NGXS, что я мог бы путать какой-то тип пути?
Обновление
Я переместил код, который устанавливает свойство currentProduct
, на все пустые значения, до первого, что делается в моем действии FetchProduct
, и теперь я получаю правильные данные, отраженные в компоненте Product$
наблюдаемый, однако в моей консоли все еще отображается множество значений, ни один из моих других наблюдаемых не ведет себя так.