Maybe this is not necessarily a reselect question
const makeSelectError = () => createSelector(
selectGlobal,
(globalState) => globalState.get('error')
);
and in reselect we use
const mapStateToProps = createStructuredSelector({
error: makeSelectError(),
});
why can't we use like below?
const makeSelectError = createSelector(
selectGlobal,
(globalState) => globalState.get('error')
);
and use like below in reselect
const mapStateToProps = createStructuredSelector({
error: makeSelectError,
});
Are there any issues/disadvantages with my code, or is that a standard practice?
Not only is the second way valid and correct it has other advantages.
With the first snippet you provide:
const makeSelectError = () => createSelector(
selectGlobal,
(globalState) => globalState.get('error')
);
makeSelectError is a factory function in that every time it is called it is returning a new and unique selector.
This means that every time a simple mapStateToProps function is called a new selector will be made and the result of the selector will be computed again.
This means that you will be losing the key benefit of reselect that is memoization.
So for simple cases you could just do the following:
const getSomePieceOfState = state => state.someArea.someDetail;
const getAnotherPieceOfState = state => state.anotherArea.anotherItem;
const getSomeCombinedState = createSelector(
getSomePieceOfState,
getAnotherPieceOfState,
(somePiece, anotherPiece) => somePiece + anotherPiece
);
const mapStateToProps = state => ({
someProp: getSomeCombinedState(state)
});
Note. it's common to prefix the name of selectors with get and to prefix the name of a selector factory (a function that returns a selector) with makeGet.
Some times creating a selector factory is necessary though if you wan't to make a selector that is dependant on a property that is not in the state.
You can read more about that here Accessing React Props in Selectors
You're doing it absolutely right in the second example. This is a standard practice.
There's no need to do wrap makeSelectError to another function.
Related
Learning redux/react-redux, I'm using useSelector with inner selector function in a separate file. It's working perfectly but I have question about best practices.
Assuming that I have a state with 3 entries (firstname, lastname, email), is it better to :
1. Have a specific selector for each case?
selector.js
export const selectFirstname = (state) => state.currentUser.firstName
export const selectLastname = (state) => state.currentUser.lastName
export const selectEmail = (state) => state.currentUser.email
component.js
const firstName = useSelector(selectFirstname)
const lastName = useSelector(selectLastname )
const email = useSelector(selectEmail)
2. Have a generic selector with param?
selector.js
export const selectItem = (key) => {
return (state) => state.currentUser[key]
}
component.js
const firstName = useSelector(selectItem('firstName'))
const lastName = useSelector(selectItem('lastName'))
const email = useSelector(selectItem('email'))
3. Have a global selector and use it with destructuring in my component?
selector.js
export const selectItem = (state) => state.currentUser
component.jsx
const {firstName, lastName, email} = useSelector(selectItem)
Thank you in advance
No, you shouldn't be doing #3. You should be returning the smallest amounts of data per the docs: https://redux.js.org/tutorials/fundamentals/part-5-ui-react#using-multiple-selectors-in-a-component
So 1 or 2 is your best option. Also, anytime currentUser changes the whole thing will re-render and if you are only using 3 of the values, why re-render the component when the values have not changed.
By the sake of understanding better createSelector, I'm trying to use it instead of useSelector
const domainEntitlements = useSelector((state) => state.objects.domainEntitlements[match.params.id]);
const domainEntitlement = createSelector((state) => state.objects.domainEntitlements, (domainEntitlements) => (domainEntitlements));
console.log(domainEntitlements, domainEntitlement);
the useSelector logs an object (correct), but the createSelector logs an equalityCheck function.
Am I doing something very stupid or why I can get the data with useSelector and not with createSelector?
CreateSelector returns a function, this function you can pass to useSelector as an argument.
CreateSelector is used to specify logic to where data is located in the state or how data is derived only once and re use them in other selectors. Here is how you can use your selector in the component:
const selectObjects = (state) => state.objects;
const selectDomainEntitlements = createSelector(
[selectObjects], //re use selectObjects
(objects) => objects.domainEntitlements
);
const createSelectDomainEntitlementById = (id) =>
createSelector(
[selectDomainEntitlements], //re use selecting domain entitlements
(domainEntitlements) => domainEntitlements[id]
);
//example of using the selector in your component
const Component = () => {
const domainEntitlement = useSelector(
createSelectDomainEntitlementById(match.params.id)
);
};
A more detailed explanation on selectors and how to possibly optimize your app by using memoization can be found here
I have a code that fetches book by its id
const fetchBook = (bookId: number) => {
const title = 'Book' + bookId;
// mimic http request
return timer(200).pipe(mapTo({ bookId, title }));
}
const bookId$ = new Subject<number>();
const book$ = bookId$.pipe(
switchMap(bookId => fetchBook(bookId)),
shareReplay(1)
);
book$.subscribe(book => console.log('book title: ', book.title))
bookId$.next(1);
I have an API method that patches values and returns the updated object:
const patchBook = (bookId: number, newTitle: string) => {
return timer(200).pipe(mapTo({ bookId, title: newTitle }));
}
What should I do to get book$ to emit the new value after I call patchBook(1, 'New Book Title')?
I can declare book$ as Subject explicitly and update it manually. But it will be imperative, not reactive approach.
Upd: The patch is called as a result of user action at any time (or never)
Upd2: Actually book$ can be also changed on server side and my real code looks like this:
const book$ = combineLatest([bookId$, currentBookChangedServerSignal$]).pipe...
The same thing you did to transform a bookId into a Book, you can use to transform a Book into a patchBook.
const book$ = bookId$.pipe(
switchMap(bookId => fetchBook(bookId)),
mergeMap(({bookId, title}) => patchBook(bookId, title)),
shareReplay(1)
);
Update:
patch is not always called
There are many ways this could be done and the "best" way really depends on how you've architected your system.
Lets say you dynamically create a button that the user clicks and this triggers an update event.
const patchBtn = document.createElement("button");
const patchBook$ = fromEvent(patchBtn, 'click').pipe(
switchMap(_ => patchBook(bookId, title))
);
const basicBook$ = bookId$.pipe(
switchMap(bookId => fetchBook(bookId))
);
const book$ = merge(patchBook$, basicBook$).pipe(
shareReplay(1)
);
You probably want your fromEvent events to emit some data rather then hard-coding (bookId, title) into the stream from a click, but you get the idea. That's just one of many ways to get the job done.
And of course, it should almost always be possible (and desirable) to remove bookId$, and replace it with a more reactive-style mechanism that hooks declarativly into whatever/wherever the ID's come from in the first place.
You can declare a fetchBook$ observable, and a patchBook$ subject. Then your book$ observable can be a merge of the two.
const patchBook = (bookId: number, newTitle: string) => {
return timer(200).pipe(
mapTo({ bookId, title: newTitle }),
tap(newBook=>this.patchBook$.next(newBook))
);
}
const bookId$ = new Subject<number>();
const fetchBook$ = bookId$.pipe(
switchMap(bookId => fetchBook(bookId)),
shareReplay(1)
);
const patchBook$ = Subject<{ bookId: number, newTitle: string}>();
const book$ = merge(fetchBook$, patchBook$);
book$.subscribe(book => console.log('book title: ', book.title))
bookId$.next(1);
patchBook(2, 'Moby Dick');
The example code for creating a selector that uses the props is as follows:
const makeMapStateToProps = () => {
const getVisibleTodos = makeGetVisibleTodos()
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props)
}
}
return mapStateToProps
}
connect(makeMapStateToProps)(Component);
However, this means the memoized selectors will be lost after the component gets unmounted (unlike the selectors defined in a file).
Is there a way in Reselect library to save these selectors?
If not, what would be the best approach of saving them? I am thinking of caching, but I would need one selector for every value.
The difference between switchMap and switchMapTo is that switchMap transforms each source emission into observable upon the emission and switchMapTo ignores emitted value and transforms each emission to an Observable that is built up during stream creation.
The thumb rule here is use switchMap when your inner stream depends on the source stream values and use switchMapTo when it doesn't.
But what if I don't care for the emission value but I do care for the emission time?
Meaning I'd like the inner Observable to be evaluated upon source Observable emission.
The obvious thing here is to use switchMap(() => Observable) but it just doesn't feel right, because of the thumb rule I mentioned before.
Example with switchMapTo (bad):
const predefinedKey = 'key';
//This need to be initialized
const obj = {};
function getObservable(key){
return Rx.Observable.of(obj[key]);
}
//This is initialization stream
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
//Would like to access the object only after initialization
const result = onInit.switchMapTo(getObservable(predefinedKey));
//Expect to see 1 in output but see 'undefined' because switchMapTo evaluated before the object is initialized
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Example with switchMap (good):
const predefinedKey = 'key';
//This need to be initialized
const obj = {};
function getObservable(key){
return Rx.Observable.of(obj[key]);
}
//This is initialization stream
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
//Would like to access the object only after initialization
const result = onInit.switchMap(() => getObservable(predefinedKey));
//Expect to see 1 in output
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
The examples are very artificial but they describe the situation pretty well.
What is the right approach here? Any other Observable function I can use for delayed execution?
Based on your example, you can use switchMapTo in combination with Observable.defer:
const predefinedKey = 'key';
const obj = {};
function getObservable(key){
return Rx.Observable.defer(() => Rx.Observable.of(obj[key]));
}
const initialize = new Rx.ReplaySubject();
initialize.next(1);
const onInit = initialize.do(val => obj[predefinedKey] = val);
const result = onInit.switchMapTo(getObservable(predefinedKey));
result.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>
Instead of deferring in getObservable, you could also defer in the switchMapTo call:
const result = onInit.switchMapTo(Rx.Observable.defer(() => getObservable(predefinedKey)));
This will just depend on the situation. That said, I also don't think there's anything wrong with using switchMap and personally, I'd probably do that instead of deferring (which is useful in other situations).