How can I alter my redux action and reducer to have array with objects in it? - react-native

I have currently redux action and reducer that allows me to add or remove items from the array. Now I want to add more items in following format. [{id: , car: '', text: '', box1Checked: '', box2Checked: '', box3Checked: ''}]
This is the form I have.
this is my current action file:
const ADD_NEW_CAR = 'ADD_NEW_CAR'
const DELETE_EXISTING_CAR = 'DELETE_EXISTING_CAR'
export const addNewCar = (text) => ({
type: ADD_NEW_CAR,
payload: text
})
export const deleteExistingCar = (car) => ({
type: DELETE_EXISTING_CAR,
payload: car
})
this is the reducer:
const ADD_NEW_CAR = 'ADD_NEW_CAR'
const DELETE_EXISTING_CAR = 'DELETE_EXISTING_CAR'
const initialState = {
cars: [],
}
const carsListReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_NEW_CAR:
return {
...state,
cars: [...state.cars, action.payload],
}
case DELETE_EXISTING_CAR:
return {
cars: [
...state.cars.filter(car => car !== action.payload)
]
}
default:
return state
}
}
export default carsListReducer
This is where i call the function to add cars.
const addCarDetails = () => {
if (newCar.length > 0) {
// setCars([
// ...cars,
// {
// id: cars.length + 1,
// license: newCar,
// },
// ])
props.addNewCar(newCar)
setValid(true)
setNewCar('')
carAddedToast()
} else {
setValid(false)
}
}
const removeCar = (item) => {
props.deleteExistingCar(item)
//setCars(cars.filter((item) => item.license !== license))
carRemovedToast()
}

To change any reducer value, you need to dispatch an action with the dispatch() method.
// Top-level import
import {useDispatch} from 'rect-redux'
// Inside a functional component
const dispatch = useDispatch()
const addCarDetails = () => {
if (newCar.length > 0) {
// dispatch add new car action to associated reducer
dispatch(props.addNewCar(newCar))
setValid(true)
setNewCar('')
carAddedToast()
} else {
setValid(false)
}
}
const removeCar = (item) => {
// dispatch remove a car action to associated reducer
dispatch(props.deleteExistingCar(item))
carRemovedToast()
}

The problem was solved using following. It was really small change that was needed.
case ADD_NEW_CAR:
return {
...state,
cars: [...state.cars, {id: state.cars.length, ...action.payload}],
}

Related

Vue 3 ref access always returns undefined

Ok. so I do have this image object I get from my DB and store it into a vuex store:
// State
const state = {
userImages: [],
};
// Getters
const getters = {
getImages: (state) => {
return state.userImages;
},
getFilePathExists: (state) => (fullPath) => {
return !!state.userImages.find((item) => item.fullPath === fullPath);
},
getFileNameExists: (state) => (name) => {
return !!state.userImages.find((item) => item.name === name);
},
getImagesInFolder: (state) => (folder) => {
if(folder) {
return state.userImages.filter(im => {
if(im.folders) {
return im.folders.includes(folder)
}
return false
})
}
return state.userImages
}
};
// Mutations
const mutations = {
setImages(state, val) {
state.userImages = val;
},
};
export default {
state,
getters,
// actions,
mutations,
};
So far so good. Now if I use the '''getImages''' getter, the Object gets to be loaded into a ref:
//...
const detailImage = shallowRef({});
const showImageDetails = (image) => {
if (!showDetails.value) {
showDetails.value = true;
}
activeImage.value = image.id
detailImage.value = image;
}
The JSON has a nested Object called exif_data. If I onsole.log this, I get: Proxy {Make: 'Apple', Flash: 'Flash did not fire. No strobe return detection fun…on present. No red-eye reduction mode or unknown.', Model: 'iPhone 6', FNumber: 2.2, COMPUTED: {…}, …}.
So far so good, but when I would like to access any property of the nested Object, either with or without using .value I just get undefined.
Any ideas?

vue apollo 2 composition api track result

So I am trying to add product impressions to my site by following this article:
https://developers.google.com/tag-manager/enhanced-ecommerce#product-impressions
I have created a bit of logic to fire off the required data like this:
import { getCurrentInstance } from "#vue/composition-api";
import { useGtm } from "#gtm-support/vue2-gtm";
export function useTrackProductImpressions(items: any[]) {
console.log("trying to track products", items);
if (!items?.length) return;
const gtm = useGtm();
if (!gtm.enabled()) return;
const dataLayer = window.dataLayer;
if (!dataLayer) return;
console.log(items);
const products = items.map((product, i) => {
const retailers = product.retailers ?? [];
return {
name: product.title, // Name or ID is required.
id: product.id,
price: retailers[0].price,
brand: product.brand,
category: product.categorySlug,
variant: product.variant,
position: i,
};
});
const instance = getCurrentInstance();
const route = instance.proxy.$route;
const routeName = route.meta?.title ?? route.name;
dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object.
dataLayer.push({
event: "productClick",
ecommerce: {
click: {
actionField: { list: routeName }, // Optional list property.
products,
},
},
// eventCallback: function () {
// document.location = productObj.url;
// },
});
}
This seems pretty normal and I have a click version of this that works fine.
The problem is, the click event can be fired when a link is clicked, this one needs to fire when the view loads, I assume in setup.
So, I have my apollo logic:
import { useQuery, useResult } from "#vue/apollo-composable";
import * as listProducts from "#graphql/api/query.products.gql";
export const defaultParameters: {
identifier?: string;
searchTerm: string;
itemsToShow: number;
page: number;
filters: any;
facets: string[];
} = {
searchTerm: "*",
itemsToShow: 12,
page: 1,
filters: [],
facets: ["Criteria/Attribute,count:100"],
};
export function useSearchProducts(params) {
const { result, loading, error, fetchMore } = useQuery(listProducts, params);
const response = useResult(result, null, (data) => data.searchProducts);
return { response, loading, error, fetchMore };
}
And from my setup I invoke like this:
const { category } = toRefs(props);
const page = ref(1);
const skip = ref(0);
const orderBy = ref([
{
key: "InVenue",
value: "desc",
},
]);
const params = computed(() => {
const filters = createFilters("CategorySlug", [category.value.slug]);
const request = createRequest(
defaultParameters,
page.value,
filters,
orderBy.value
);
return { search: request };
});
const { response, loading, error} = useSearchProducts(params);
Which I can then return to the template like this:
return { response, loading, error };
Now I have done this, I want to add some tracking, so initially I did this:
watch(response, (result) => useTrackProductImpressions(result?.value?.items));
But it was always undefined.
I added console log on result within the watch method and it is always undefined.
So I changed to this:
const track = computed(() => {
useTrackProductImpressions(response.value.items);
});
But this never gets invoked (I assume because it has no return value and I don't use it in the template).
My question is, which is the best way to do what I am attempting? Am I missing something or am I on the write track?
I think I was close, I just used the computed property to return my products like this:
const products = computed(() => {
if (!response.value) return [];
useTrackProductImpressions(response.value.items);
return response.value.items;
});
const total = computed(() => {
if (!response.value) return 0;
return response.value.total;
});
const hasMoreResults = computed(() => {
if (!response.value) return false;
return response.value.hasMoreResults;
});
return {
products,
loading,
error,
total,
hasMoreResults,
skip,
more,
search,
};

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
};

How to fix the navigation

I’m setting up a new service for navigation to "Chat" service but it doesn't do anything and I don't know why.
This is for a new component
here is the "onCancel" button that use the "getCustomerService" function..
handleUnrecognizedUser = () => {
const infoMsg = {
onCancel: getCustomerService
};
};
here is the "getCustomerService" function that get called
import { AppStore, RoutingStore } from '../../stores'
import call from 'react-native-phone-call'
callServeiceCenter = (number) => {
const args = {
number, // String value with the number to call
prompt: false // Optional boolean property. Determines if the user should be prompt prior to the call
}
return call(args).catch(console.error)
}
export default getCustomerService = () => {
if (AppStore.isWorkingHours)
RoutingStore.goTo('Chat')
else {
callServeiceCenter(AppStore.getCallCenterPhone)
}
}
this is for the "RoutingStore" :
import { observable, action, computed } from "mobx";
import { NavigationActions, StackActions, DrawerActions } from 'react-navigation'
class RoutingStore {
#observable nav = null;
#observable PrevPage = null;
#observable curentPage = null;
#observable isGoBackAvailable = true;
#observable isLoggedIn = false;
#action
setNavigation(data) {
this.nav = data
}
goTo = (data, _params) => {
let { routeName, params } = data
const navigateAction = NavigationActions.navigate(
routeName
? { routeName, params }
: { routeName: data, params: { ..._params } })
this.nav.dispatch(navigateAction)
}
#action
goBack = () => {
}
#action
updateCurrentPage(data) {
this.curentPage = data
}
#action
updatePrevPage(data) {
this.PrevPage = data
}
updatePages = (prev, cur) => {
this.updatePrevPage(prev)
this.updateCurrentPage(cur)
}
#action
setLoggedIn(status) {
this.isLoggedIn = status
}
#action
openDrawer() {
this.nav.dispatch(DrawerActions.openDrawer())
}
#action
closeDrawer() {
this.nav.dispatch(DrawerActions.closeDrawer())
}
#action
toggleDrawer() {
this.nav.dispatch(DrawerActions.toggleDrawer())
}
disableLoginRoute = (route) => {
const resetAction = StackActions.reset({
index: 0,
key: null,
actions: [NavigationActions.navigate({ routeName: route })],
});
this.nav.dispatch(resetAction)
}
isGoBackAllowed = () => {
switch (this.curentPage) {
case "Tabs": return false
case "Login": return false
default: return this.goBack()
}
}
#computed
get isNonLogin() {
return this.isLoggedIn;
}
#computed
get getCurentPage() {
return this.curentPage;
}
}
const routingStore = new RoutingStore();
export default routingStore;
I expect to navigate to the Chat as well.

When an object's state changes another object's state changes also in react native redux

I am trying to hold user info with defaultUser as default state after fetching. But If user state changes with UPDATEUSERSTATE, defaultUser also changes. I could not understand that behaivour
Firstly fetching the data from restApi
Updating user state on MainComponent
If User changes textinput on ModalView, updating the user state.
const userReducer = (state = {} ,action) => {
switch (action.type) {
case actionTypes.GETUSERINFOBYUSERNAME_SUCCESS:
return {
...state,
isFetching: action.isFetching,
error: action.error,
user: action.user,
defaultUser:action.user,
open: true
};
case actionTypes.UPDATEUSERSTATE:
return {
...state,
user: action.user
}
default:
console.log("[userReducer]: defalt state");
return state;
}
};
//ACTIONS
export const getUserInfoByUserNameSuccess=(user) => {
return {type: actionTypes.GETUSERINFOBYUSERNAME_SUCCESS, isFetching: true, error: null, user: user}
}
export const updateUserState=(user) => {
return {type: actionTypes.UPDATEUSERSTATE, user:user}
}
//CALLING GETUSERINFO
this.props.onGetUserInfoByUserName(val);
const mapDispatchToProps = dispatch => {
return{
onGetUserInfoByUserName : userName => dispatch(getUserInfoByUserNameFetching(userName))
};
};
//AND CALLING UPDATEUSERSTATE
textChangedHandler = (key,value) => {
let user = this.props.user;
user[key]=value;
this.props.onUpdateUserState(user);
}
const mapDispatchToProps = dispatch => {
return{
onUpdateUserState : (user) => dispatch(updateUserState(user))
};
};
The problem here is that you're directly setting the values of two separate objects to reference the same object (both user and defaultUser are being set to reference object action.user). So if you change the value of one of the objects, the reference changes which changes the second object.
Redux doesn't replace the object with a new one, but rather does a shallow copy of the new values. See the below snippet for an example:
var actionUser = { foo: 1 }
var defaultUser = actionUser
var user = actionUser
user.bar = 2
console.log(actionUser)
// { foo: 1, bar: 2 }
console.log(defaultUser)
// { foo: 1, bar: 2 }
console.log(user)
// { foo: 1, bar: 2 }
To fix this you can use the Object.assign() method to assign the references to a new object. See below snippet:
var actionUser = { foo: 1 }
var defaultUser = Object.assign({}, actionUser)
var user = Object.assign({}, actionUser)
user.bar = 2
console.log(actionUser)
// { foo: 1 }
console.log(defaultUser)
// { foo: 1 }
console.log(user)
// { foo: 1, bar: 2 }
So whenever you assign a new value from your actions to any of your state objects, use Object.assign().
Example (from your code):
case actionTypes.GETUSERINFOBYUSERNAME_SUCCESS:
return {
...state,
user: Object.assign({}, action.user),
defaultUser: Object.assign({}, action.user),
};
case actionTypes.UPDATEUSERSTATE:
return {
...state,
user: Object.assign({}, action.user),
}