get type in withlatestFrom - redux

I have the following selector
export const featureAdapter: EntityAdapter<IRoute> = createEntityAdapter<IRoute>({
selectId: model => model.routeId,
});
export interface State extends EntityState<IRoute> {
selectedRouteId: string;
selectedPointId: string;
}
export const selectAllEntities: (state: object) => Dictionary<IRoute> = featureAdapter.getSelectors(selectRouteState).selectEntities;
export const selectedR: MemoizedSelector<object, string> = createSelector(selectRouteState, getSelectedRoute);
export const selectedRoute: MemoizedSelector<object, IRoute> = createSelector(
selectAllEntities,
selectedR,
(entities, id) => entities[id]
);
that return something of type IRoute
In my effect I use a withlatest from
onAction$ = this.actions$.pipe(
ofType<featureActions.onAction>(featureActions.ActionTypes.onAction),
concatMap(action =>
of(action).pipe(
withLatestFrom(this.store$.pipe(select(RoutesStoreSelectors.getById(), {routeId: action.payload.routeId}))),
)
),
switchMap(([action, route]) => {})
)
inside the
switchMap(([action, route])
the route variable, is of type any. but it should be of type IRoute
how can I make it work correctly ?

I can't see why TypeScript cannot infer the type, but you can always explicitly specify the type.
onAction$ = this.actions$.pipe(
ofType<featureActions.onAction>(featureActions.ActionTypes.onAction),
concatMap(action =>
of(action).pipe(
withLatestFrom(this.store$.pipe(select(RoutesStoreSelectors.getById(), {routeId: action.payload.routeId}))),
)
),
switchMap(([action, route]: [Action, IRoute]) => {})
)

I am not sure why you need get hold of dispatched action in switchMap, but you can do the following to get the route of type IRoute -
onAction$ = this.actions$.pipe(
ofType<featureActions.onAction>(featureActions.ActionTypes.onAction),
switchMap(action =>
combineLatest(of(action), this.store$.pipe(select(RoutesStoreSelectors.getById(), {routeId: action.payload.routeId})))
),
switchMap(([action, route]) => {})
)
Having code like this will ensure to have the action of type of the dispatched action and route is of type IRoute.
I hope it helps.

Related

How do i can use concatLastestFrom?

I am try use effects with my store, but 2 days i have this error:
Idk why it happened, can u help me please?
stackblitz simulation
Action:
export const LoadMeetings = createAction('[ Meetings/Api ] Load Meetings');
Effect:
readonly loadMeetings$ = createEffect(() =>
this.actions$.pipe(
ofType(fromMeetingsActions.LoadMeetings),
concatLatestFrom(action => this.store.select(fromMeetings.selectMeetingsState)),
)
);
Selector:
export const selectMeetingsState =
createFeatureSelector<MeetingsStateModel>(meetingsFeatureKey);
//This doesn't work too
const selector = <T>(mapping: (state: MeetingsStateModel) => T) => createSelector(selectMeetingsState, mapping);
export const selectSelectedMeetingId = selector((state) => state.selectedMeetingId);
I try find answer in google, docs, youtube, q&a and any services, but zero feed back..
I try change operator map, concatMap, switchMap, pipe and another..
I try change selectors, but zero result
Effects usually map to an action ie:
readonly loadMeetings$ = createEffect(() =>
this.actions$.pipe(
ofType(fromMeetingsActions.LoadMeetings),
concatLatestFrom(action => this.store.select(fromMeetings.selectMeetingsState)),
map(() => fromMeetings.loadMeetingsSucceeded)
),
);
Stackblitz: https://stackblitz.com/edit/angular-pq8vlw?file=src%2Fmeetings%2F%2Bstate%2Feffects.ts
If they don't return an action you need to add { dispatch: false }:
readonly loadMeetings$ = createEffect(() =>
this.actions$.pipe(
ofType(fromMeetingsActions.LoadMeetings),
concatLatestFrom((action) =>
this.store.select(fromMeetings.selectMeetingsState)
)
),
{ dispatch: false }
);
Stackblitz: https://stackblitz.com/edit/angular-h2yifk?file=src%2Fmeetings%2F%2Bstate%2Feffects.ts

Redux: Cannot read property 'add' of undefined

I'm hard fighting with Redux, trying to dispatch inputValue to a store. When I'm trying to do this I get Cannot read property 'add' of undefined error.
import React, { useState } from 'react';
import './SearchingBar.css'
import { connect } from 'react-redux'
import actions from '../../duck/actions'
const SearchingBar = ({shareToggleClass, props}) => {
const [toggleClass, setToggleClass] = useState(false);
const [query, setQuery] = useState('');
const search = event => {
if(event.key === "Enter") {
setToggleClass(true);
shareToggleClass({toggleClass})
props.add(query)
}
}
return (
<input type = "text"
placeholder = "Search.."
className = {toggleClass ? "Active" : "unActive"}
onChange = {e => setQuery(e.target.value)}
value = {query}
onKeyPress = {search}
/>
)
}
const mapStateToProps = state => ({
inputValue: state.inputValue
})
const mapDispatchToProps = dispatch => ({
addValue: inputValue => dispatch(actions.addValue(inputValue))
})
export default connect(mapStateToProps, mapDispatchToProps)(SearchingBar);
Pretty sure your component argument destructuring is wrong.
Currently, you have:
const SearchingBar = ({shareToggleClass, props}) => {
However, as written, this assumes that the one-and-only argument for this function, which is an object we normally refer to as props, also has a nested field named props. That's probably not correct.
If you're trying to grab out just the shareToggleClass field, and then access all remaining props field as an object named props, you should use "rest destructuring":
// The ... rest operator grabs all remaining fields, and puts them in a new object
const SearchingBar = ({shareToggleClass, ...props}) => {
Alternately, you could just destructure any remaining fields specifically that you need:
const SearchingBar = ({shareToggleClass, add}) => {
The other issue is that you likely don't have a props.add function anyway, because your mapDispatch is configured to create a prop named addValue:
const mapDispatchToProps = dispatch => ({
addValue: inputValue => dispatch(actions.addValue(inputValue))
})
Also got a couple suggestions on the React-Redux usage.
If you are going to use connect, we recommend using the "object shorthand" form of mapDispatch instead of writing it as a function:
const mapDispatch = {
addValue: actions.addValue
}
connect(mapState, mapDispatch)(MyComponent)
// or even just pass the `actions` object directly
connect(mapState, actions)(MyComponent)
But, we specifically recommend using the React-Redux hooks API as the default instead of connect:
const SearchingBar = () => {
const inputValue = useSelector(state => state.inputValue);
const dispatch = useDispatch()
const [toggleClass, setToggleClass] = useState(false);
const [query, setQuery] = useState('');
const search = event => {
if(event.key === "Enter") {
setToggleClass(true);
shareToggleClass({toggleClass})
dispatch(addValue(query))
}
}
return (
<input type = "text"
placeholder = "Search.."
className = {toggleClass ? "Active" : "unActive"}
onChange = {e => setQuery(e.target.value)}
value = {query}
onKeyPress = {search}
/>
)
}

Cannot create valid state with payload for reducer from ngrx documentation

I have problem to update state in ngrx reducer for action loadSuccess
State after load action
{
dashboard: {
serverList: null,
isLoading: true,
error: 'zero'
}
}
State after loadSuccess action
{
dashboard: {
isLoading: false,
error: 'zero'
}
}
const t1 = (state, action) => {
return ({...state, serverList: action.payload.servers});
};
const reducer = createReducer(initialState,
on(load, state => ({...state, isLoading: true})),
on(loadSuccess, (state, {servers}) => ({...state, serverList: servers})),
// on(loadSuccess, t1)
);
export function dashboardReducer(state: State | undefined, action: Action) {
return reducer(state, action);
}
when I swap on(loadSuccess.... line with t1 state it works. I actually check what the object in parameter is and fetch value what I want. But why it fails for on(loadSuccess, (state, {servers}) => ({...state, serverList: servers}))?
Actions definition
export const load = createAction('[Dashboard Component] Load');
export const loadSuccess = createAction('[Dashboard Component] LoadSuccess', props<{servers: ServerInfo[]}>());
should you share how you define the loadSuccess action?
looks like you have the payload property and should use it as payload.servers.
2nd Parameter of the given function contains the action, not the payload..
on(loadSuccess, (state, {payload}) => ({...state, serverList: payload.servers})),
I found that problem came from ngrx docs, from the definition of effect.
#Injectable()
export class MovieEffects {
loadMovies$ = createEffect(() => this.actions$.pipe(
ofType('[Movies Page] Load Movies'),
mergeMap(() => this.moviesService.getAll()
.pipe(
map(movies => ({ type: '[Movies API] Movies Loaded Success', payload: movies })),
catchError(() => EMPTY)
))
)
);
constructor(
private actions$: Actions,
private moviesService: MoviesService
) {}
}
Action here is defined as { type: '[Movies API] Movies Loaded Success', payload: movies }
After change to use directly defined above action export const loadSuccess = createAction('[Dashboard Component] LoadSuccess', props<{servers: ServerInfo[]}>()); it works
loadSomething$ = createEffect(() => this.actions$.pipe(
ofType(DashboardActions.load),
mergeMap(() => this.service.getServers().pipe(map(servers => Actions.loadSuccess({serverList: servers})))),
catchError(() => of({type: '[Dashboard Component] ServerInfo Loaded Error'}))
)
);

I can't display a single entity object with ngrx

Hello I use ngrx and spring boot to display all products. But I can't display one selected product.
My getProductAction et effect work well.
This is the result in Redux:
Some help please
This is my state and initialState:
export interface ProduitState extends EntityState<Produit>{
isLoading: boolean;
selectedProduitId: any;
error: any;
produits: any;
searchQuery: string;
}
enter image description here
export const produitAdapter: EntityAdapter<Produit> = createEntityAdapter<Produit>({
selectId: (produit: Produit) => produit.id,
sortComparer: false,
});
export const produitInitialState: ProduitState = produitAdapter.getInitialState({
isLoading: true,
selectedProduitId: null,
error: null,
produits: [],
searchQuery: ''
});
export const selectedProduitId = (state: ProduitState) => state.selectedProduitId;
export const selectIsLoading = (state: ProduitState) => state.isLoading;
export const selectError = (state: ProduitState) => state.error;
export const selectSearchQuery = (state: ProduitState) => state.searchQuery;
This my action:
export const getProduitAction = createAction('[Produit] Get Produit', props<{produitId: string}>());
const produitPayload = props<{produit: Produit}>();
export const getProduitSuccessAction = createAction('[Produit] Get Produit Success', produitPayload);
This is my reducer:
on(ProduitActions.getProduitAction, (state, {produitId}) => ({
...state, selectedProduitId: undefined, isLoading: true, isLoaded: false, error: null
})),
This is my effect:
getProduit$ = createEffect(() =>
this.actions$.pipe(
ofType(getProduitAction),
exhaustMap(action => {
alert("getProduit Effects "+ action.produitId);
return this.produitService.getProduit(action.produitId)
.pipe(
//tap(res => console.log(res + "TAG EEEEE")),
map((produit, id) => getProduitSuccessAction({produit})),
catchError(err => this.handleError(err))
)
})
)
);
I do this code to get de current selected id:
public produitsState = createFeatureSelector<state.ProduitState>('produit');
private selectors = state.produitAdapter.getSelectors(this.produitsState);
private selectCurrentProduitId = createSelector(this.produitsState, state.selectedProduitId);
private isLoading = createSelector(this.produitsState, state.selectIsLoading);
private error = createSelector(this.produitsState, state.selectError);
private searchQuery = createSelector(this.produitsState, state.selectSearchQuery);
I do this code to get de current selected product but it doesn't work:
getCurrentProductSelected() {
//console.log("ProduitStore: getCurrentProductSelected()");
//console.log(this.store.select(this.selectCurrentProduitId));
alert(this.getProducts());
return combineLatest(
this.getProducts(),
this.store.select(this.selectCurrentProduitId),
(products, selectedId) => selectedId.map(id => {
alert(id +" getCurrentProductSelected");
alert(products +" getCurrentProductSelected");
return products[id];
})
);
}
I try to get the product but I have nothing:
let id = this.route.snapshot.paramMap.get('id');
this.store.dispatchGetProduitAction(id);
this.produit$ = this.store.getCurrentProductSelected();
this.produit$.pipe(
map(produit =>{
alert(produit.nom + " this.produit$");
})
);
Help please. Thanks
It does not look like you set 'selectedProduitId' anywhere. And without that, how would 'getCurrentProductSelected' return anything. If i understand your idea correctly, you need to do something like this:
on(ProduitActions.getProduitAction, (state, {produit}) => ({
...state, selectedProduitId: produit.id
})),
But it's just a blind guess.

How to get a entity by ID ngrx

I have the following store
export const featureAdapter: EntityAdapter<IProduct> = createEntityAdapter<IProduct>({
selectId: model => model.id,
});
export interface State extends EntityState<IProduct> {
selectedProduct?: IProduct;
isLoading?: boolean;
error?: any;
}
export const initialState: State = featureAdapter.getInitialState({
selectedProduct: IProduct,
isLoading: false,
error: null
});
I would like that my selected product always point on a Entity and get updates with it. I believe since actions are always creating new object, the link is not possible, I decided to change the selectedProduct from a reference to a simple id.
export const initialState: State = featureAdapter.getInitialState({
selectedProduct: string,
isLoading: false,
error: null
});
but how do i retrieve my entity with the same ID, and get updates on my observable if it is changed ?
I tried
export const getSelectedProduct: MemoizedSelector<object, any> = createSelector(
selectAllEntities,
selectedProduct,
(entities, id) => entities[id]
);
export const selectEntityById = createSelector(
selectAllEntities,
(entities, props) => entities[props.id]
);
my questions are
Is this the only way to select an entity by id ?
this.store$.pipe(
select(ProductStoreSelectors.selectEntityById, { id: product.id }),
map(product => console.log(product))
)
and
this.store$.select(ProductStoreSelectors.getSelectedProduct).subscribe((product: string) => {
console.log(product)
}),
this never trigger when I change my selected product Id
EDIT :
the reducer on select do the following
const SET_SELECTED_PRODUCT = (state: State, action: featureAction.SetSelectedProduct) => {
return {
...state,
selectedProduct: action.payload.product.id
};
};
Try something like this in your selector:
export const selectEntityById = (id: number) => createSelector(
selectAllEntities,
(entities) => entities[id]
);
and your select:
select(ProductStoreSelectors.selectEntityById(id))

Resources