ngrx/store - 'throw error as Cannot read property' - ngrx

I am implementing the createFeatureSelector and createSelector - but getting a error as core.js:15714 ERROR TypeError: Cannot read property 'showProductCode' of undefined
I use "#ngrx/store": "^7.1.0",
But not able to find the issue. here is my code :
import { Product } from "./../product";
import * as fromRoot from "./../../state/app.state";
import { createFeatureSelector, createSelector } from "#ngrx/store";
export interface State extends fromRoot.State {
products:ProductState
}
export interface ProductState {
showProductCode : boolean;
currentProduct : Product;
products:Product[]
}
const initialState:ProductState = {
showProductCode : true,
currentProduct:null,
products:[]
}
const getProductFeatureState = createFeatureSelector<ProductState>("product");
export const getShowProductCode = createSelector(
getProductFeatureState,
state => state.showProductCode
);
export const getCurrentProduct = createSelector(getProductFeatureState, state => state.currentProduct);
export const getProducts = createSelector(getProductFeatureState, state => state.products);
export function reducer(state=initialState, action):ProductState {
switch (action.type) {
case "TOGGLE_PRODUCT_CODE":
return {
...state,
showProductCode : action.payload
}
default:
return state;
}
}

you have a typo here:
const getProductFeatureState = createFeatureSelector<ProductState>("product");
"products" is what you've defined and you're selecting "product"

Related

NGRX Select returning Store not a value

catalogueSelection in Store Image
I have the data I require in state (catalogueSelection: searchTextResult & categoryCheckboxResult) and need to pass the string 'SearchTextResult' into one component and the Array 'categoryCheckboxResult' into another.
When I try to retrieve the required values I am retrieving the whole store. I have looked at numerous websites and entries here but getting very confused now.
Model:
export class SearchTextResult {
searchTextResult: string;
}
export class CategoryCheckboxResult {
categoryCheckboxResult:Array<CategoryCheckboxResult>;
}
Actions:
import { Action } from '#ngrx/store';
import { SearchTextResult, CategoryCheckboxResult } from 'app/#core/services/products/products.model';
export enum UserCatalogueSelectionTypes {
AddSearchTextResult = '[SearchTextResult] AddResult',
AddCategoryCheckboxResult = '[CategoryCheckboxResult] AddResult',
GetSearchTextResult = '[SearchTextResult] GetResult',
}
export class AddSearchTextResult implements Action {
readonly type = UserCatalogueSelectionTypes.AddSearchTextResult;
constructor(public payload: SearchTextResult){
}
}
export class AddCategoryCheckboxResult implements Action {
readonly type = UserCatalogueSelectionTypes.AddCategoryCheckboxResult;
constructor(public payload: CategoryCheckboxResult){
}
}
export class GetSearchTextResult implements Action {
readonly type = UserCatalogueSelectionTypes.GetSearchTextResult;
}
export type UserCatalogueSelectionUnion =
| AddSearchTextResult
| AddCategoryCheckboxResult
| GetSearchTextResult
Reducers:
import { SearchTextResult, CategoryCheckboxResult} from "app/#core/services/products/products.model";
import { UserCatalogueSelectionTypes, UserCatalogueSelectionUnion} from "../actions/products.actions";
export interface UserCatalogueSelectionState {
searchTextResult: SearchTextResult | null;
categoryCheckboxResult: CategoryCheckboxResult | null;
}
export const initialState: UserCatalogueSelectionState = {
searchTextResult: null,
categoryCheckboxResult: null,
}
export function reducer(state:UserCatalogueSelectionState = initialState, action: UserCatalogueSelectionUnion ): UserCatalogueSelectionState{
switch (action.type) {
case UserCatalogueSelectionTypes.AddSearchTextResult:
return {
...state,
searchTextResult: action.payload,
};
case UserCatalogueSelectionTypes.AddCategoryCheckboxResult:
return {
...state,
categoryCheckboxResult: action.payload,
};
case UserCatalogueSelectionTypes.GetSearchTextResult: {
return state;
}
default: {
return state;
}
}
}
Selectors:
import { createSelector,createFeatureSelector } from "#ngrx/store";
import {UserCatalogueSelectionState} from '../../store/reducer/products.reducer';
export const fetchSearchTextResults = createFeatureSelector<UserCatalogueSelectionState>("searchTextResult");
export const fetchSearchTextResult = createSelector (
fetchSearchTextResults,
(state:UserCatalogueSelectionState) => state.searchTextResult.searchTextResult
);
export const fetchCatalogueCheckBoxResults = createFeatureSelector<UserCatalogueSelectionState>("catalogueCheckboxResult");
export const fetchCatalogueCheckBoxResult = createSelector (
fetchCatalogueCheckBoxResults,
(state: UserCatalogueSelectionState) => state.categoryCheckboxResult.categoryCheckboxResult
);
My Component 1
Observable:
public searchTextResult: Observable<String>;
Contructor: (part of)
private store: Store<fromCatalogueSelection.UserCatalogueSelectionState>
Code Snippet: (asking for the data)
this.searchTextResult = this.store.select('SearchTextResult');
console.log('TESTING SEARCH TEXT: ', this.searchTextResult);
Console:
TESTING SEARCH TEXT: StoreĀ {_isScalar: false, actionsObserver: ActionsSubject, reducerManager: >ReducerManager, source: Store, operator: DistinctUntilChangedOperator}
My Component 2
Observable
searchTextResult$: Observable<CatalogueSelectionActions.GetSearchTextResult>;
Code Snippet: (asking for the data)
this.searchTextResult$ = this.store.select('GetSearchTextResult');
console.log('TESTING SEARCH TEXT: ', this.searchTextResult$);
Console:
TESTING SEARCH TEXT: StoreĀ {_isScalar: false, actionsObserver: ActionsSubject, reducerManager: > ReducerManager, source: Store, operator: DistinctUntilChangedOperator}
I've given up on the Selectors for the moment. Any help much appreciated as I'm going a round in circles.
You are almost there, the value from the console log is the observable object, everytime you select something from the store, you will get the value wrapped within an observable. You just need to subscribe to it:
this.searchTextResult$ = this.store.select('GetSearchTextResult');
this.searchTextResult$.subscribe((yourData) => console.log(yourData));
Also, since you are working with selectors, use them, you don't have to write the state/selector name:
selector
...
export const fetchCatalogueCheckBoxResult = createSelector (
fetchCatalogueCheckBoxResults,
(state: UserCatalogueSelectionState) =>
state.categoryCheckboxResult.categoryCheckboxResult
);
component
import * as YourSelectors from './store/something/selectors/yourthing.selectors'
...
...
this.searchTextResult$ = this.store
.select(YourSelectors.fetchCatalogueCheckBoxResult)
.subscribe(console.log);
Additionally, try to subscribe using the async pipe delegating that to your template html so you don't have to deal with the subscription in the code, for example:
component
...
export class Component {
searchTextResult$!: Observable<any> // your data type here
...
...
this.searchTextResult$ = this.store
.select(YourSelectors.fetchCatalogueCheckBoxResult)
}
html
<ng-container *ngIf="(searchTextResult$ | async) as result">
<p>Your result value: {{ result }}</p>
</ng-container>

Expected 0 arguments, but got 1. with Redux toolkit

I am currently trying to build a simple example of redux using redux/toolkit. I am not sure what I am doing wrong.
This is my store.
import { configureStore } from "#reduxjs/toolkit";
import reducer from "./workouts";
export default function () {
return configureStore({ reducer });
}
Here is my workouts.ts file
import { createAction } from '#reduxjs/toolkit'
export interface IWorkout {
id: number
restTime: number
workoutTime: number
exercises: number[]
numReps: number
}
export interface IState {
workouts: IWorkout[]
}
export interface IAction {
type: string
payload: IWorkout[] | any
}
export const workoutAdded = createAction('workoutAdded')
export const workoutDeleted = createAction('workoutDeleted')
const defaultState: IState = {
workouts: [],
}
let lastId = 0
const reducer = (state: IState = defaultState, action: IAction): IState => {
switch (action.type) {
case workoutAdded.type:
return {
...state,
workouts: [
...state.workouts,
{
id: ++lastId,
restTime: action.payload.restTime,
workoutTime: action.payload.workoutTime,
exercises: action.payload.exercises,
numReps: action.payload.numReps,
},
],
}
case workoutDeleted.type:
return {
...state,
workouts: state.workouts.filter((workout) => workout.id !== action.payload.id),
}
default:
return state
}
}
export default reducer
And here is my index.ts file
import { render } from "react-dom";
import configureStore from "./store/store";
import { workoutAdded } from "./store/workouts";
import App from "./App";
const store = configureStore();
const newWorkout = {
restTime: 10,
workoutTime: 30,
exercises: [1, 2, 3],
NumReps: 6
};
store.dispatch(workoutAdded(newWorkout));
const rootElement = document.getElementById("root");
render(<App />, rootElement);
My error appears in index.ts file in this line:
store.dispatch(workoutAdded(newWorkout));
Not sure what I am missing. I reproduced the error in this sandbox
This is the error that I am getting in the console:
workoutAdded has 0 arguments, if you want to add arguments, you can implement Action
export class workoutAdded implements Action {
readonly type = "workoutAdded"
constructor(public payload: Workout){}
}
This fixed the issue. createAction can receive as a second argument a callback function that returns an object with the payload.
export const workoutAdded = createAction("workoutAdded", (workout) =>{
return { payload: workout };
});

NGRX; How to apply filter on adapter data

I have this piece of code which I want to apply a filter on. The state uses EntityState and Adapter.
getAllObjects needs to get categories with parentId of 13 and the getAllTextures gets the remaining.
import { Category } from './models/category';
import { createFeatureSelector, createSelector } from '#ngrx/store';
import { AppState } from './app-state';
import { EntityState } from '#ngrx/entity';
import * as fromCategory from './services/reducers/category.reducer';
export interface CategoriesState extends EntityState<Category> {
allCategoriesLoaded: boolean;
}
const getCategories = createFeatureSelector<AppState, CategoriesState>('categories');
export const getAllObjects = createSelector(getCategories, fromCategory.selectAll); // filter(x => x.parentId === 13)
export const getAllTextures = createSelector(getCategories, fromCategory.selectAll); // filter(x => x.parentId !== 13)
export const getAllCategoriesLoaded = createSelector(getCategories, state => state.allCategoriesLoaded);
and this is the reducer:
import {createReducer, on, State} from '#ngrx/store';
import { EntityState, EntityAdapter, createEntityAdapter } from '#ngrx/entity';
import { Category } from 'src/app/models/category';
import { RefreshCategoryDone, CategoryActionTypes, CategoryActions } from '../actions/category.actions';
import { CategoriesState } from 'src/app/categories-state';
export const categoryAdapter: EntityAdapter<Category> = createEntityAdapter<Category>();
export const initialState: CategoriesState = categoryAdapter.getInitialState({allCategoriesLoaded : false});
export function categoryReducer(state = initialState, action: CategoryActions) {
switch (action.type) {
case CategoryActionTypes.LoadCategories:
categoryAdapter.addMany(action.payload, state);
break;
default:
return state;
}
}
export const {
selectAll,
selectEntities,
selectIds,
selectTotal
} = categoryAdapter.getSelectors();
Is it possible to select data from adapter by condition? If so, how?
Here's a few simple steps to set this up:
1) add a 'selectedId' property to your State and set the initial state to null
export interface State extends EntityState<Category> {
selectedCategoryId: string | null;
}
export const adapter: EntityAdapter<Category> = createEntityAdapter<Category>({
selectId: (category: Category) => category.id,
sortComparer: false,
});
export const initialState: State = adapter.getInitialState({
selectedCategoryId: null,
});
2) Add actions to set the selected ID
// in the reducer, the action does following:
on(ViewReportPageActions.selectCategory, (state, { id }) => ({
...state,
selectedCategoryId: id,
})),
3) add a selector for the selectedId in the reducer
export const getSelectedId = (state: State) => state.selectedReportId;
4) add a selector at your FeatureSelector file, this is where you add the filter
// you've implemented another name for you Feature Selector here
export const getDataState = createFeatureSelector<State, DataState>('data');
export const getCategoryEntitiesState = createSelector(
getDataState,
state => state.categories
);
export const getSelectedCategoryId = createSelector(
getCategoryEntitiesState,
fromCategorys.getSelectedId
);
export const getSelectedCategory = createSelector(
getCategoryEntities,
getSelectedCategoryId,
(entities, selectedId) => {
if (selectedId != "0"){
return selectedId && entities[selectedId];
} else {
//this is what I use, when I want to create a new category.
//I set the selectedId to 0 which indicitates I'm creating a new one
return selectedId && generateMockCategory();
}
}
);
export const getNotSelectedCategory = createSelector(
getCategoryEntities,
getSelectedCategoryId,
(entities, selectedId) => {
// You'll have to test this...
return entities.filter(e => e.id !== selectedID);
}
);
example pages:
Reducer file;
FeatureSelector Page reference function
'selectSelectedBook'
Found the answer:
import { Category } from './models/category';
import { createFeatureSelector, createSelector } from '#ngrx/store';
import { AppState } from './app-state';
import { EntityState } from '#ngrx/entity';
import * as fromCategory from './services/reducers/category.reducer';
import { map } from 'rxjs/operators';
export interface CategoriesState extends EntityState<Category> {
allCategoriesLoaded: boolean;
}
const getCategories = createFeatureSelector<AppState, CategoriesState>('categories');
export const getAllCategories = createSelector(getCategories, fromCategory.selectAll);
export const getAllObjects = createSelector(getAllCategories, (categories) => categories.filter(x => x.categoryId !== 13));
export const getAllTextures = createSelector(getAllCategories, (categories) => categories.filter(x => x.categoryId === 13));
export const getAllCategoriesLoaded = createSelector(getCategories, state => state.allCategoriesLoaded);

I was trying to get input with redux,all is fine but I can't figure out how to get input values

Trying to get user input with action,all is working i get my console.logs about how inputVal changes,but when I try to print this in i get undefined in console
Should I use like mapDispatchToProps or I don't need this,since I'm passing actions as second param into mapStateToProps
actions:
export const inputChange = val => {
return {
type: INPUT_CHANGE,
payload: val
};
};
reducer:
import { INPUT_CHANGE } from './actionTypes';
const initialState = {
inputVal: ''
};
export default (state = initialState, action) => {
switch (action.type) {
case INPUT_CHANGE:
return {
...state,
inputVal: action.payload
};
default:
return state;
}
};
mainPage:
const mapStateToProps = state => {
console.log(state);
return state;
};
class MainPage extends Component {
onInput = e => {
this.props.inputChange(e.target.value);
console.log(this.props.inputChange(e.target.value));
};
render() {
console.log(this.props.inputVal);
return (
<div>
<input onChange={this.onInput}></input>
<p>{this.props.}</p>
</div>
);
}
}
export default connect(
mapStateToProps,
{
addToCart,
removeFromCart,
selectItem,
inputChange
}
)(MainPage);
combinedReducers:
import { combineReducers } from 'redux';
import AddItem from './addItem/reducer';
import InputReducer from './reducerInput';
export default combineReducers({
AddItem,
InputReducer
});
I've tried to this.props.inputVal.
Since you have combineReducers, you should use these keys to access in mapStateToProps.
From the redux docs:
The state produced by combineReducers() namespaces the states of each
reducer under their keys as passed to combineReducers()
You can control state key names by using different keys for the
reducers in the passed object. For example, you may call
combineReducers({ todos: myTodosReducer, counter: myCounterReducer })
for the state shape to be { todos, counter }.
So your mapStateToProps must be like:
const mapStateToProps = state => {
console.log(state);
return {
inputVal: state.InputReducer.inputVal
}
};
A minimal working code sandbox:
https://codesandbox.io/s/cold-meadow-pxtu3

Redux combineReducer returns default state for reducer not called in action

I'm new to react redux, so I think I'm just missing something basic.
I have three reducers, two to handle orders that update in the store as arrays, and one that shows the status of a web socket connection I'm using to receive orders from the server.
// reducers.js
import { combineReducers } from 'redux'
import { ADD_POS_ORDER, ADD_MOBILE_ORDER, UPDATE_WS_STATUS, wsStatuses } from '../actions/actions'
const { UNINITIALIZED } = wsStatuses
const posOrders = (state = [], action) => {
switch (action.type) {
case ADD_POS_ORDER:
return [
...state,
{
id: action.order.id,
status: action.order.status,
name: action.order.name,
pickupNum: action.order.pickupNum
}
]
default:
return state
}
}
const mobileOrders = (state = [], action) => {
switch (action.type) {
case ADD_MOBILE_ORDER:
return [
...state,
{
id: action.order.id,
status: action.order.status,
name: action.order.name,
pickupNum: action.order.pickupNum
}
]
default:
return state
}
}
const wsStatus = (state = UNINITIALIZED, action) => {
switch (action.type) {
case UPDATE_WS_STATUS:
return action.status
default:
return state
}
}
const displayApp = combineReducers({
posOrders,
mobileOrders,
wsStatus
})
export default displayApp
When I connect to the socket, I dispatch an action to update wsStatus and the action is stored as 'CONNECTED'.
When I follow with an order with the posOrders reducer, the wsStatus is reset to its default, 'UNINITIALIZED'.
What I am struggling to understand is why wsStatus is not using the previous state of 'CONNECTED', but instead returning default.
// actions.js
export const UPDATE_WS_STATUS = 'UPDATE_WS_STATUS'
export const wsStatuses = {
UNINITIALIZED: 'UNINITIALIZED',
CONNECTING: 'CONNECTING',
CONNECTED: 'CONNECTED',
DISCONNECTED: 'DISCONNECTED'
}
export const ADD_POS_ORDER = 'ADD_POS_ORDER'
export const ADD_MOBILE_ORDER = 'ADD_MOBILE_ORDER'
export const UPDATE_POS_ORDER = 'UPDATE_POS_ORDER'
export const setWsStatus = (status) => {
return {
type: 'UPDATE_WS_STATUS',
status: status
}
}
export const updateOrderQueue = (action, order) => {
return {
type: action,
id: order.id,
order: order,
receivedAt: Date.now()
}
}
Here's where I make the calls:
// socketListeners.js
import { setWsStatus } from '../actions/actions'
import SockJS from 'sockjs-client'
export const socket = new SockJS('http://localhost:3000/echo')
export default function (dispatch, setState) {
socket.onopen = function () {
dispatch(setWsStatus('CONNECTED'))
}
socket.onclose = function () {
dispatch(setWsStatus('DISCONNECTED'))
}
}
// orders container
import React, { Component } from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { socket } from '../helpers/socketListeners'
import { updateOrderQueue, setWsStatus } from '../actions/actions'
import PosOrder from '../components/queue/PosOrder'
class PosOrderList extends Component {
constructor(props) {
super(props)
}
componentDidMount() {
const { dispatch } = this.props
socket.onmessage = function(e) {
// convert order info to object
let parsedOrder = JSON.parse(e.data)
let action = parsedOrder.action
let order = parsedOrder.order
dispatch(updateOrderQueue(action, order))
}
}
render() {
const { updateOrderQueue } = this.props
return (
<ul>
{this.props.posOrders.map(posOrder =>
<PosOrder
key={posOrder.id}
{...posOrder}
/>
)}
</ul>
)
}
}
PosOrderList.propTypes = {
posOrders: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.hash,
status: PropTypes.string,
name: PropTypes.string,
pickupNum: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
}))
}
// send data to component props
const mapStateToProps = (state) => {
return {
posOrders: state.posOrders,
}
}
export default connect(mapStateToProps)(PosOrderList)
// store
const store = configureStore(initialState)
export default function configureStore(initialState) {
return createStore(
displayApp,
initialState,
applyMiddleware(
createLogger({
stateTransformer: state => state.toJS()
}),
thunk,
// socketMiddleware
)
)
}
addSocketListeners(store.dispatch, store.getState)
Lastly, the store logs here: redux store
Any and all help on this would be very appreciated! Thank you!
When you compose your reducer with combineReducers, for each dispatched action, all subreducers get invoked, since every reducer gets a chance to respond to every action.
Therefore, all state gets initialized after the first action is dispatched.
Your reducers are working fine https://jsfiddle.net/on8v2z8j/1/
var store = Redux.createStore(displayApp);
store.subscribe(render);
store.dispatch({type: 'UPDATE_WS_STATUS',status:'CONNECTED'});
store.dispatch({type: 'ADD_POS_ORDER',id:'id'});
store.dispatch({type: 'UPDATE_WS_STATUS',status:'DISCONNECTED'});

Resources