-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(dispatch-data-to-store): add function overloads and allow SSOT by using selector #165
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -1,37 +1,159 @@ | ||||
import { Store } from '@ngrx/store'; | ||||
import { Observable, throwError } from 'rxjs'; | ||||
import { catchError, finalize, tap } from 'rxjs/operators'; | ||||
import { Observable, of, throwError } from 'rxjs'; | ||||
import { catchError, finalize, switchMap, tap } from 'rxjs/operators'; | ||||
import { | ||||
BaseStoreActions, | ||||
BaseStoreSelectors, | ||||
EntityStoreActions, | ||||
EntityStoreSelectors, | ||||
} from '../../interfaces'; | ||||
|
||||
// ------------------- | ||||
// Overloads | ||||
// ------------------- | ||||
// BASE STORE | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: BaseStoreActions<DataType>, | ||||
selectAction: BaseStoreSelectors<DataType>, | ||||
data: Observable<DataType>, | ||||
store: Store, | ||||
dispatchType: 'set' | ||||
): Observable<DataType>; | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: BaseStoreActions<DataType>, | ||||
selectAction: BaseStoreSelectors<DataType>, | ||||
data: Observable<DataType>, | ||||
store: Store | ||||
): Observable<DataType>; | ||||
// NO SELECTOR | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: BaseStoreActions<DataType>, | ||||
selectAction: null | undefined, | ||||
data: Observable<DataType>, | ||||
store: Store, | ||||
dispatchType: 'set' | ||||
): Observable<null>; | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: BaseStoreActions<DataType>, | ||||
selectAction: null | undefined, | ||||
data: Observable<DataType>, | ||||
store: Store | ||||
): Observable<null>; | ||||
// ENTITY STORE | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: EntityStoreActions<DataType>, | ||||
selectAction: EntityStoreSelectors<DataType>, | ||||
data: Observable<DataType[]>, | ||||
store: Store, | ||||
dispatchType: 'set' | 'add' | 'update' | ||||
): Observable<DataType[]>; | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: EntityStoreActions<DataType>, | ||||
selectAction: EntityStoreSelectors<DataType>, | ||||
data: Observable<DataType[]>, | ||||
store: Store | ||||
): Observable<DataType[]>; | ||||
// NO SELECTOR | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: EntityStoreActions<DataType>, | ||||
selectAction: null | undefined, | ||||
data: Observable<DataType[]>, | ||||
store: Store, | ||||
dispatchType: 'set' | 'add' | 'update' | ||||
): Observable<null>; | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: EntityStoreActions<DataType>, | ||||
selectAction: null | undefined, | ||||
data: Observable<DataType[]>, | ||||
store: Store | ||||
): Observable<null>; | ||||
|
||||
// ------------------- | ||||
// Definition | ||||
// ------------------- | ||||
/** | ||||
* Dispatches data to the store based on a provided action and Observable. Loading and error state will be handled by default. | ||||
* | ||||
* @param dispatchAction - The action we wish to use to dispatch data to the store | ||||
* @param selectAction - The selector we wish to use to select the data from the store. If not provided, `null` will be returned. | ||||
* @param data - The data we wish to dispatch to the store | ||||
* @param store - The store we wish to dispatch the data to | ||||
* @param dispatchType - Whether we wish to set, add or update the data. By default this is `set` | ||||
* @return {*} {Observable<DataType>} | ||||
* @returns {Observable<(DataType | DataType[] | null)>} - Returns the data that was dispatched to the store. The generic type is the same as the `data`. | ||||
*/ | ||||
export const dispatchDataToStore = <DataType>( | ||||
dispatchAction: any, | ||||
data: Observable<DataType>, | ||||
export function dispatchDataToStore<DataType = any>( | ||||
dispatchAction: BaseStoreActions<DataType> | EntityStoreActions<DataType>, | ||||
selectAction: BaseStoreSelectors<DataType> | EntityStoreSelectors<DataType> | undefined, | ||||
data: Observable<DataType | DataType[]>, | ||||
store: Store, | ||||
dispatchType: 'set' | 'add' | 'update' = 'set' | ||||
): Observable<DataType> => { | ||||
): Observable<DataType | DataType[]> | null { | ||||
// Iben: Set the loading state to true and the error state to false to start a new set | ||||
store.dispatch(dispatchAction.loading({ payload: true })); | ||||
store.dispatch(dispatchAction.error({ payload: false })); | ||||
|
||||
// Iben: Set, add or update the data according to the provided dispatch type | ||||
return data.pipe( | ||||
tap((payload) => { | ||||
if (dispatchType === 'set') { | ||||
store.dispatch(dispatchAction.set({ payload })); | ||||
} else if (dispatchType === 'add') { | ||||
store.dispatch(dispatchAction.add({ payload })); | ||||
} else { | ||||
store.dispatch(dispatchAction.update({ payload })); | ||||
switch (dispatchType) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that a switch case or an if else structure in this case makes no difference, I'd keep it the way it is if it isn't necessary to change it. Changing these kinds of things in a PR without real cause makes it harder to review PRs, because you have to first try to figure out why it was done in relation to the changes, and then realize it wasn't really needed to do so. |
||||
case 'set': { | ||||
return payload instanceof Array | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're making too big of an assumption here. Yes, These checks will have to go, because they're too strongly set. |
||||
? store.dispatch( | ||||
(dispatchAction as EntityStoreActions<DataType>).set({ | ||||
payload, | ||||
}) | ||||
) | ||||
: store.dispatch( | ||||
(dispatchAction as BaseStoreActions<DataType>).set({ | ||||
payload, | ||||
}) | ||||
); | ||||
} | ||||
case 'add': { | ||||
if (!(payload instanceof Array)) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check is also not really correct. If you would have an array of arrays in your store, you can no longer use the add method. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How so? An array of an array is still of type Array. Since we negate the result and then throw an error, the user is forced to provide an array when using [[]] instanceof Array // true The type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, I don't remember what I was referring to tbh 😅 |
||||
return throwError( | ||||
() => | ||||
new Error( | ||||
'NgxStore: Payload must be an array when using "add". Also make sure your storeslice is of type "EntityStoreAssets".' | ||||
) | ||||
); | ||||
} | ||||
|
||||
return store.dispatch( | ||||
(dispatchAction as EntityStoreActions<DataType>).add({ payload }) | ||||
); | ||||
} | ||||
case 'update': { | ||||
if (payload instanceof Array) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same for this check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps I am incorrect here, but shouldn't the payload of the
Type |
||||
return throwError( | ||||
() => | ||||
new Error( | ||||
'NgxStore: Payload must not be an array when using "update".' | ||||
) | ||||
); | ||||
} | ||||
|
||||
return store.dispatch( | ||||
(dispatchAction as EntityStoreActions<DataType>).update({ | ||||
payload, | ||||
}) | ||||
); | ||||
} | ||||
} | ||||
}), | ||||
switchMap((payload) => { | ||||
if (!selectAction) { | ||||
return of(null); | ||||
} | ||||
|
||||
// Wouter: Get the value recently set to the store. This is done to keep the Store as SSOT. | ||||
return store.select( | ||||
payload instanceof Array | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's better to run a check on the selectAction rather than on the payload, because again, that's not possible with how the store works. You'll be better off doing something like |
||||
? (selectAction as EntityStoreSelectors).selectAll | ||||
: (selectAction as BaseStoreSelectors).select | ||||
); | ||||
}), | ||||
// Iben: Catch the error and dispatch it to the store | ||||
// Iben: Catch the error and dispatch its to the store | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course, no idea how that got changed! 😅 |
||||
catchError((err) => { | ||||
store.dispatch(dispatchAction.error({ payload: err })); | ||||
|
||||
|
@@ -42,4 +164,4 @@ export const dispatchDataToStore = <DataType>( | |||
store.dispatch(dispatchAction.loading({ payload: false })); | ||||
}) | ||||
); | ||||
}; | ||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not that familiar with these overloads, but what will be the effect of putting the selectAction here on the existing implementation?
It's one thing that the result of this PR forces you to change the return types, but completely changing the order of this implementation forcing you to pass undefined as a second parameter would make it a lot harder to work wit.
I'd personally expect the overload to be either with the dispatchAction and one without it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that would be possible, I presume! 👍