NgRX 8 is not working in my Angular 8 with a failure: 'toJSON failed on Type' - angular8

I have been trying since yesterday to add the new NgRX 8 to my Angular application. But I am stuck on one thing and I don't know what the problem is.
I have tried to build a very simple example for movies:
export class Movie {
public id: number;
public name: string;
constructor() {}
}
My actions are:
export const getAll = createAction('[Movies Page] Load Movies');
export const getAllSuccess = createAction('[Movies Page] Load Movies Success', props<{ payload: Movie[] }>());
export const getAllFail = createAction('[Movies Page] Load Movies Fail', props<{ error: any }>());
And my effect is:
#Injectable()
export class MovieEffects {
constructor(private actions$: Actions) {
}
loadMovies$ = createEffect(() =>
this.actions$.pipe(
ofType(getAll),
switchMap(() =>
of([movies_array]).pipe(
map(result => getAllSuccess({ payload: result })),
catchError(error => of(getAllFail({ error })))
)
)
)
);
}
I have added all of action cases to my reducer:
export interface AppState<T> {
list: T[];
isNull: boolean;
count: number;
type: StateType;
}
export const initialState: AppState<Movie> = {
type: StateType.Loading,
isNull: true,
count: 0,
list: new Array<Movie>()
};
const movieRed = createReducer(
initialState,
on(MovieActionTypes.getAll, state => ({
...state,
type: StateType.Loading
})),
on(MovieActionTypes.getAllSuccess, (state: AppState<Movie>, action) => ({
...state,
type: StateType.Loaded,
isNull: action.payload == null,
count: action.payload != null ? action.payload.length : 0,
list: action.payload
})),
on(MovieActionTypes.getAllFail, state => ({
...state,
type: StateType.Error
}))
);
export function movieReducer(state: AppState<Movie> = initialState, action) {
return movieRed(state, action);
}
And added the effects forRoot and reducers forRoot in my module.
Everything works fine. But when I am trying to read the state using:
this.movies$ = store.pipe(select('list'));
I result is always undefined and I see this message in my DevTool
I have no idea where the problem is. I have tried many solutions, none has worked for me.
Does anybody know why and how to fix it?

I have found the answer. I have missed defining the AppState.
export interface AppState {
movieReducer: ReducerState<Movie>;
}
and in the module:
StoreModule.forFeature('movieReducer', movieReducer),

Related

Redux reducer state returns an empty Object in component

I am fairly new to React Native and Redux. I am fetching data on Restaurants from an API route using axios,and my goal is to display the Restaurants, but when I console.log in the components I get an empty object {}.
Here is my data sample:
[{
"id_supplier":1,
"supplier_name":"Grill Park",
"supplier_description":"Order food at Grill-Park. Masters of Italian Pizza.",
"supplier_image":"restaurant1.jpg",
"createdAt":"2020-08-28T21:00:00.000Z",
"updatedAt":"0000-00-00"
},
{
"id_supplier":2,
"supplier_name":"Bobbies burger",
"supplier_description":"Order a burger on the go. Beef, Cheese, Hamburger, Vegan Burgers",
"supplier_image":"restaurant2.jpg",
"createdAt":"2020-08-28T21:00:00.000Z",
"updatedAt":"0000-00-00"
},
{
"id_supplier":3,
"supplier_name":"Chicken n licken",
"supplier_description":"Renowned for serving tasty fried chicken pieces and a wide range of other menu items.",
"supplier_image":"restaurant3.jpg",
"createdAt":"2020-08-28T21:00:00.000Z",
"updatedAt":"0000-00-00"
},
{
"id_supplier":4,
"supplier_name":"French House Tarvern",
"supplier_description":"Grab a sandwich at our Pick 'n' Mix Sandwich hip and fun environment",
"supplier_image":"restaurant4.jpg",
"createdAt":"2020-08-28T21:00:00.000Z",
"updatedAt":"0000-00-00"
}]
Here is my model:
export interface RestaurantModel {
id_supplier: number;
supplier_name: string;
supplier_description: string;
supplier_image: string;
createdAt: Date;
updatedAt: Date;
}
export interface RestaurantState{
restaurants_avail: RestaurantModel;
//add others
}
My action file looks as follows
export const getRestaurants = ()=>{
return async(dispatch : Dispatch<RestaurantAction>) =>{
try{
const response = await axios.get<RestaurantModel>(`${BASE_URL}/restaurants`)
console.log("respond now", response.data)//This returns an array of the restaurants
if(response){
dispatch({
type: 'ON_GET_RESTAURANTS',
payload: response.data
})
} else {
dispatch({
type: 'ON_RESTAURANT_ERROR',
payload: 'RESTAURANTS UNAVAILABLE'
})
}
}catch(error){
dispatch({
type: 'ON_RESTAURANT_ERROR',
payload: error
})
}
}
}
When I console log the value of of the response.data an array of the restaurants is returned.
My reducer code looks as follows
const initialState: RestaurantState = {
restaurants_avail: {} as RestaurantModel,
}
const RestaurantReducer = (state: RestaurantState = initialState, action: RestaurantAction) =>{
switch(action.type){
case 'ON_GET_RESTAURANTS':
console.log(action.payload)//this returns the updated state as expected
return{
...state,
restaurants_avail: action.payload
}
default:
return state
}
}
I have all my reducers combined in rootreducer then my store looks as follows.
const store = createStore(rootReducer,composeWithDevTools(
applyMiddleware(thunk))
)
console.log("Value of store state", store.getState())
export {store};
store.getState() returns empty objects.
Here is how I intend to print out the array in my Component.
interface HomeProps {
restaurantReducer: RestaurantState;
getRestaurants: Function;
}
const _HomeScreen: React.FC<HomeProps> = (props) => {
const { restaurants_avail } = props.restaurantReducer;
console.log("restaurants array",restaurants_avail)//This returns an empty object
useEffect(() => {
console.log(props.getRestaurants())//This return an array object with all the restaurants
})
I don't understand where am going wrong as console.log(restaurants_avail) should print out the array, but it instead returns an empty {}
Here is the React Native Debugger state chart.

undefined is not a function (near '...action.account.map...')

I keep getting this error and I don't know why. All the code seems to be right to me but I can't see my mistake. I'm new to React-Native and New to Redux. I would really appreciate it if you could help me.
const initialState = {
account: [],
}
const addAccount = (state = initialState, action) => {
switch (action.type) {
case SET_ACCOUNT:
return {
account: action.account.map(
(cb) => newAccount = { id: cb.id.toString(), name: cb.name, imageUri: cb.imageUri, email: cb.email, password: cb.password })
}
default:
return state
}
}
It should be
action.payload.account.map
Action has two properties payload and type.
type is used in the switch and you send objects as the payload

ngrx store state undefined

I am not sure why my state in my store is undefined when I try to access it. I have been looking at this for sometime now and I cannot figure it out.
my actions are
export const GetMerchants = createAction('[Merchant] - Get Merchants');
export const GetMerchantsSuccess = createAction(
'[Merchant] - Get Merchants Success',
props<{ payload: Merchant[] }>()
);
export const GetMerchantsFailure = createAction(
'[Merchant] - Get Merchants Failure',
props<{ payload: Error }>()
);
My reducers and state def are
export default class MerchantListState {
merchants: Array<Merchant>;
merchantError: Error;
}
export const initializeMerchantListState = (): MerchantListState => {
return {
merchants: new Array<Merchant>(),
merchantError: null
};
};
export const intialMerchantListState = initializeMerchantListState();
const _reducer = createReducer(
intialMerchantListState,
on(actions.GetMerchants, (state: MerchantListState) => {
return {
...state
};
}),
on(actions.GetMerchantsSuccess, (state: MerchantListState, { payload }) => {
let newstate = { ...state,
merchants: [ ...state.merchants, payload],
merchantError: null
};
return newstate;
}),
on(actions.GetMerchantsFailure, (state: MerchantListState, { payload }) => {
console.log(payload);
return { ...state, merchantError: payload };
}),
);
export function merchantListReducer(state: MerchantListState, action: Action) {
return _reducer(state, action);
}
My effects
#Injectable()
export class MerchantListEffects {
constructor(private apiService: ApiService, private apiRouteService: ApiRouteService, private action$: Actions) { }
GetMerchants$: Observable<Action> = createEffect(() =>
this.action$.pipe(
ofType(actions.GetMerchants),
mergeMap(action => this.apiService.get(this.apiRouteService.toMerchants()).pipe(
map((data: Merchant[]) => { console.log(data); return actions.GetMerchantsSuccess({ payload: data }); }
), catchError((error: Error) => { return of(actions.GetMerchantsFailure({ payload: error })) })
)
)));
}
When I inject the state into the component
private store: Store<{ merchantList: MerchantListState }>
I get an undefined merchant$ observable when I try to do this
this.merchants$ = store.pipe(select('merchantList'));
this.merchantSubscription = this.merchants$.pipe(
map(x => {
console.log(x.merchants);
})
)
.subscribe();
On a button click I am loading the merchants with this dispatch
this.store.dispatch(actions.GetMerchants());
I have my reducer and effects defined in AppModule
StoreModule.forRoot({ merchantList: merchantListReducer }),
EffectsModule.forRoot([MerchantListEffects])
Is it something that I am missing?
First Parameter of createReducer is a value, not a function.
API > #ngrx/store
createReducer
If you use a function, you have to call it:
const _reducer = createReducer(
intialMerchantListState()
I prefare the way to define direct a value initialState:
export const initializeMerchantListState: MerchantListState = {
merchants: new Array<Merchant>(),
merchantError: null
};

Why am I getting "redux-persist/createPersistoid: error serializing state"?

I have no idea why I'm getting this error. there's no file referenced no line referenced. I only know the action thats executed which results in the error.
The full error:
console.error "redux-persist/createPersistoid: error serializing
state", TypeError: JSON.stringify cannot serialize cyclic structures.
Here is the action that causes the error:
export const loadInitialDiscover = () => {
return (dispatch) => {
dispatch({ type: types.DISCOVER_LOADING });
return getNewest(dispatch);
};
};
const getNewest = (dispatch) => {
return firebase
.firestore()
.collection('users')
.where('role', '==', 'a')
.where('active', '==', true)
.orderBy('createdAt')
.limit(10)
.get()
.then((querySnapshot) => {
const newest = [];
querySnapshot.forEach((queryDocSnapshot) => {
const profile = queryDocSnapshot.data();
newest.push(profile);
});
dispatch({ type: types.LOAD_NEWEST, payload: newest });
})
.catch((err) => loadFail(dispatch, err.message));
};
implementation of action in screen:
componentDidMount () {
//console.log('INITIAL REDUX STATE: ' + JSON.stringify(this.props.newest));
this.props.newest == null ? this.props.loadInitialDiscover() : null;
}
notice this .stringify() is commented out
Ive removed literally any and every existing JSON.stringify, deleted build folder, restarted bundler, restarted simulator. the error persists.
here is relevant reducer:
const INITIAL_STATE = {
newest: null,
loading: false,
error: ''
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.DISCOVER_LOADING:
return {
...state,
loading: true,
error: ''
};
case types.LOAD_FAIL:
return {
...state,
loading: false,
error: action.info
};
case types.LOAD_NEWEST:
return {
...state,
loading: false,
error: '',
newest: action.payload
};
}
}
and here's the persist config:
const persistAuthConfig = {
key: 'auth',
storage: AsyncStorage
};
const persistUserConfig = {
key: 'user',
storage: AsyncStorage
};
const persistDiscoverConfig = {
key: 'discover',
storage: AsyncStorage
};
const AppReducer = combineReducers({
auth: persistReducer(persistAuthConfig, AuthReducer),
user: persistReducer(persistUserConfig, UserReducer),
discover: persistReducer(persistDiscoverConfig, DiscoverReducer)
});
export default AppReducer;
How do I get this error gone?
The Firestore query response contains cyclic data (ref parameter) that is not possible to serialised by JSON.stringify hence the error from Redux-Persist, (yes; redux-persist use it under the hood). Just remove the ref parameter containing the cyclic data from all the objects inserted into the array newest array.
Also, do a check on other parameters that cannot be serialised by JSON.stringify like ref.
The below may help you to achieve this:-
const newest = []; querySnapshot.forEach((queryDocSnapshot) => {
const profile = queryDocSnapshot.data();
const {ref, ...profileSerialisable} = profile;
newest.push(profileSerialisable);
});
Please refer here for more discussions based on this.

Calling redux actions without bindActionCreator

I want to store the GPS position of the user in my redux-store. To get the coords I use this:
navigator.geolocation.watchPosition( (data) => {
//
}, null, {
timeout: 60000,
distanceFilter: 10
});
I have a reducer for the position:
import createReducer from '../lib/createReducer'
import * as types from '../actions/types'
export const position = createReducer({}, {
[types.SET_POSITION](state, action) {
return {
latitude: action.latitude,
longitude: action.longitude
};;
}
})
And an action:
import * as types from './types'
export function watchPosition() {
return (dispatch, getState) => {
???
}
}
export function setPosition({ latitude, longitude }) {
console.log('JA');
return {
type: types.SET_POSITION,
latitude,
longitude
}
}
I want to init this watchPosition in my Home-Screen. I don't bind the actions there (no connect() ).
How to call this action and init the reducer when new position is available?
You czn import your store object in your commponent or another executive place of code and then use store.dispatch(() => { return{ type: ACTION_TYPE, payload: data } }).
or you can use reduxThunk middleware to touch the store: store.dispatch({ type: ACTION_TYPE, payload: data }).
Hope this help you