redux : Select/get nested State by dynamic keys - redux

Here my redux state , the state has dynamic nested object name
const search = {
client :
{ result: [],
selected: null,
isLoading: false,
isSuccess: false,},
[dynamicKey] :
{ result: [],
selected: null,
isLoading: false,
isSuccess: false,},
[dynamicKey2] :
{ result: [],
selected: null,
isLoading: false,
isSuccess: false,}
};
I'm trying to get nested object by dynamic key , here is my selector code :
import { createSelector } from "reselect";
export const searchState = (state) => state.search;
export const selectSearch = (keyRef) =>
createSelector([searchState], (search) => search[keyRef]);

You forgot to ask the question but your code looks fine as it is. In the component you can use useMemo to not needlessly create the selector:
//renamed the selector to create...
export const createSelectSearch = (keyRef) =>
createSelector([searchState], (search) => search[keyRef]);
//compnent example
const MyComponent = ({keyRef}) => {
const selectSearch = React.useMemo(
()=>createSelector(keyRef),//create the selector when keyRef changes
[keyRef]
);
const result = useSelector(selectSearch)
return <jsx />
}
Some more information about this pattern can be found here

Related

How to fix this.TD[this.j5] is null using Flexmonster

I work on local environment and I use Next like framework. I use flexmonster component for react (https://www.npmjs.com/package/react-flexmonster)
When I make some modifications the current flexmonster component shows me this error : this.TD[this.j5] is null
I know that Flexmonster works on CSR (client side rendering) and I used a custom debounce hook to wait before excute any flexmonster functions .
Flexmonster component code :
import { useRef } from 'react';
import dynamic from 'next/dynamic';
import useDebounce from '#hooks/useDebounce';
import 'flexmonster/flexmonster.css';
const DynamicFlexMonster = dynamic(() => import('react-flexmonster'), {
ssr: false,
});
const Flexmonster = ({
dataSource = [],
rows = [],
columns = [],
measures = [],
formats = {},
viewType,
gridType,
chartType,
}) => {
const flexmonsterDataStructure = {
dataSource: {
data: dataSource,
},
slice: {
rows,
columns,
measures,
},
options: {
viewType,
grid: {
type: gridType,
showHeader: false,
showTotals: false,
showGrandTotals: 'off',
},
chart: {
type: chartType,
},
showEmptyData: true,
},
formats,
};
const ref = useRef(null);
const [debounceReport, setDebounceReport] = useDebounce(null);
const onReportComplete = () => {
setDebounceReport(ref.current, 1000);
if (debounceReport) {
console.log('>>>>', ref.current.flexmonster.getReport());
}
};
return (
<>
<DynamicFlexMonster
ref={ref}
toolbar={false}
width="100%"
report={flexmonsterDataStructure}
reportcomplete={onReportComplete}
/>
</>
);
};
export default Flexmonster;
I was facing the same issue, the main problem is Flexmonster use the window element so when you are working with an SSR framework like Nextjs you will need to call the all page where you display your Flexmonster component as SSR: false.
From your code, you will have to call your Flexmonster component like this:
/* pages/my-flexmonster-component-page */
import dynamic from 'next/dynamic';
const Flexmonster = dynamic(() => import('#components/Flexmonster'), {
ssr: false,
});
export default function MyFlexmonsterComponentPage(){
return (
<>
<Flexmonster/>
</>
);
}

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))

Redux initial state values becomes undefined

Below is my initial default state values,
export const parameterInitialState = {
id: '',
name: '',
parameterSettings: parameterSettingsInitialState,
parameterMediaSettings: parameterMediaSettingsInitialState,
};
export const parameterSettingsInitialState = {
attributeId: '',
value: '',
};
export const parameterMediaSettingsInitialState = {
barcodeSupported: false,
mediaTypeId: '',
qrcodeSupported: false,
scratchOffPINSupported: false,
};
in the parameterState reducer, I use the above parameterInitialState
const initialState = {
parameterSettings: parameterInitialState,
};
export default function parameterState( state = initialState, action ) {
switch ( action.type ) {
case FETCH_PARAMETER_SUCCESS:{
return { ...state, ...action.payload };
}
default:
return state;
}
}
But my issue is in the initial state the object parameterSettings and parameterMediaSettings becomes undefined
Change the order in which your initial state variables are defined, in the given order variables parameterSettingsInitialState and parameterMediaSettingsInitialState will return undefined.
So make sure the child object variables are defined first followed by the parent object you plan include them in.
export const parameterSettingsInitialState = {
attributeId: '',
value: '',
};
export const parameterMediaSettingsInitialState = {
barcodeSupported: false,
mediaTypeId: '',
qrcodeSupported: false,
scratchOffPINSupported: false,
};
export const parameterInitialState = {
id: '',
name: '',
parameterSettings: parameterSettingsInitialState,
parameterMediaSettings: parameterMediaSettingsInitialState,
};
Updated answer withparameterSettingsInitialState and parameterMediaSettingsInitialState as arrays :
export const parameterSettingsInitialState = [{
attributeId: '',
value: '',
}];
export const parameterMediaSettingsInitialState = [{
barcodeSupported: false,
mediaTypeId: '',
qrcodeSupported: false,
scratchOffPINSupported: false,
}];
export const parameterInitialState = {
id: '',
name: '',
parameterSettings: parameterSettingsInitialState,
parameterMediaSettings: parameterMediaSettingsInitialState,
};

Reselect Cannot read property 'get' of undefined

I am using reselect and react redux. I am trying to make a selector for a basic modal implementation.
my selector is
const selectModal = (state) => state.get('modal');
which throws the error of
Cannot read property 'get' of undefined
edit: It has been requested I show how I call select modal, though it should make no difference.
const mapStateToProps = createStructuredSelector({
isVisible: selectModalIsVisible(),
});
const mapDispatchToProps = {
hideModal,
showModal
};
export default connect(mapStateToProps, mapDispatchToProps)(Modal);
I believe this means the modal state container is not being found
Perhaps I am setting up my reducer or store incorrectly. My reducer is
function modalReducer(state = initialState, action) {
switch (action.type) {
case HIDE_MODAL:
return state.set(
'isVisible', false);
case SHOW_MODAL:
return state.set(
'isVisible', true);
default:
return state;
}
}
which is combined with combine reducers into a glob
export default function createReducer(asyncReducers){
return combineReducers({
route: routeReducer,
auth: authReducer,
modal: modalReducer,
...asyncReducers
});
}
and then injected into my store
function configureStore(initialState = {}, history) {
const middlewares = [
sagaMiddleware,
routerMiddleware(history),
];
const enhancers = [
applyMiddleware(...middlewares),
]
const store = createStore(
createReducer(),
fromJS(initialState),
compose(...enhancers)
);
store.runSaga = sagaMiddleware.run;
//store.close = () => store.dispatch(END)
store.runSaga(sagas);
store.asyncReducers = {};
return store;
}
var initialState = {}
const store = configureStore(fromJS(initialState), browserHistory);
The error within reselect is at lines 73/74 params = dependencies.map
var selector = function selector(state, props) {
for (var _len4 = arguments.length, args = Array(_len4 > 2 ? _len4 - 2 : 0), _key4 = 2; _key4 < _len4; _key4++) {
args[_key4 - 2] = arguments[_key4];
}
var params = dependencies.map(function (dependency) {
return dependency.apply(undefined, [state, props].concat(args));
});
return memoizedResultFunc.apply(undefined, _toConsumableArray(params));
};
So what am I doing wrong, do I need to do something with immutableJS differently to access the modal, or is my setup for the app incorrect? Thank you in advance for your feedback.
If you're using selectModal like you're using selectModalIsVisible, then your syntax is wrong. I'm pretty sure createStructuredSelector does not understand () => (state) => state.get('modal'). It would only accept (state) => state.get('modal')
Typically, my usages of createStructuredSelector will look like either
const getThing = (state, props) => state.things[props.thingId];
const getModal = state => state.get('modal');
const mapStateToProps = createStructuredSelector({
thing: getThing, // notice no parens
modal: getModal, // notice no parens
})
OR if I need selector factories:
// just pretend this selector was more complicated and needed memoization
const makeGetThing = () => createSelector(
state => state.things,
(state, props) => props.thingId,
(things, thingId) => things[thingId]);
const getModal = state => state.get('modal');
const makeMapStateToProps = () => createStructuredSelector({
thing: makeGetThing(), // yes parens
modal: getModal, // no parens
})

Resources