TL;DR: I'm trying to use an id and type parameters in my selectors but the params are undefined
. What is the correct way of doing this using reselect
and createStructuredSelector
?
I'm writing selectors using reselect
to get the data needed for a React component. The data is stored in a key/value format where the key is made up of a dynamic id and type value. The object looks like this:
customView: {
'viewBy:deviceProfileId&id:5923f82a-80c2-4c88-bd0e-c105ad989ab2': { // type = 'deviceProfileId' & id = '5923f82a-80c2-4c88-bd0e-c105ad989ab2'
list: { ... },
meta: { ... },
error: { ... }
}
Users enter the id and type and trigger the data fetching from API. I have the basic selector but I'm stuck trying to get the data needed for each entry using these dynamic values - they continue to come out undefined. What am I missing? Is there a better way to accomplish this?
// reducer.js : Fn to generate key
export const customViewKey = ({ customViewType, id } = {}) => {
const typeKey = (customViewType && `viewBy:${customViewType}`) || '';
const idKey = (id && `id:${id}`) || '';
const namespace = `${typeKey}&${idKey}`;
return `${namespace}`;
};
// selector.js
const getCustomView = ({ customView }) => customView; // Basic selector works
export const getCustomViewData = createSelector(
getCustomView,
(customView, id, customViewType) => { // id and customViewType are undefined
return customView && customView[customViewKey({ customViewType, id })];
}
);
// index.js
export const CustomViewsPage = ({ classes, getCustomView, customViewMap, customViewType, id }) => {
const [idValue, setIdValue] = useState('');
const [type, setType] = useState('');
const handleTypeChange = (e) => {
let typeEntered = e ? e.value : '';
setType(typeEntered);
};
const handleInputChange = (e) => {
setIdValue(e.target.value);
};
const handleSubmit = (e) => { // Fn called when user hits submit
e.preventDefault();
getCustomView({ id: idValue, customViewType: type });
};
return (
<div>
<form onSubmit={handleSubmit} className={classes.customViewForm}>
<div>
<p>Select type of custom view:</p>
<Select
placeholder="View messages by"
options={customViewTypes}
onChange={handleTypeChange}
isClearable
/>
</div>
<div>
<p>Enter ID for device, project or device profile:</p>
<InputBase
placeholder="5923f82a-80c2-4c88-bd0e-c105ad989ab2"
required
onChange={handleInputChange}
fullWidth
/>
</div>
<label htmlFor="create-custom-view-btn">
<Input id="create-custom-view-btn" type="submit" />
<Button
component="span"
variant="outlined"
color="primary"
endIcon={<SendRounded />}
onClick={handleSubmit}
>
Create view
</Button>
</label>
</form>
</div>
const mapDispatchToProps = {
getCustomView: requestCustomView,
};
const mapStateToProps = createStructuredSelector({
customViewMap: getCustomViewMap,
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(CustomViewsPage));
I figured out the solution thanks to @markerikson's answer and this blog.
These are the selectors I'm using:
const getCustomView = ({ customView }) => customView;
const getId = (_, id) => id;
const getCustomViewType = (_, id, customViewType) => customViewType;
export const getCustomViewData = createSelector(
getCustomView,
getId,
getCustomViewType,
(customView, id, customViewType) => {
return customView && customView[customViewKey({ customViewType, id })]; // WORKS!
}
);
export const getCustomViewMap = createSelector(getCustomViewData, getDomainMap);
And I'm using useSelector
to call the selector from my component this way:
const selectedCustomView = useSelector(state => getCustomViewMap(state, idValue, type));