-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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 : withLifecycleCallbacks support for wilcard * and array of callbacks + refacto #9577
Conversation
This looks great 👍 I like that it encourages good practices and better readability. Would you mind adding some tests and updating the documentation? |
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.
As this is a new feature, it must be PRed against next
rather than master
@@ -67,13 +67,13 @@ import { | |||
* [ | |||
* { | |||
* resource: "posts", | |||
* afterRead: async (data, dataProvider) => { | |||
* afterRead: async (data, {dataProvider, resource}) => { |
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.
This is a breaking change. You can add the same feature in a backward compatible way, by adding a third parameter instead.
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.
changed
|
||
const afterUpdateHandlers = handlers.filter( | ||
h => h.resource === resource && h.afterUpdate | ||
newParams = await applyCallbacks(resource, 'beforeSave', newParams); |
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.
This is another breaking change: beforeSave used to be applied on params.data, not on params
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.
changed
} | ||
const afterSaveHandlers = handlers.filter( | ||
h => h.resource === resource && h.afterSave | ||
newParams = await applyCallbacks(resource, 'beforeSave', newParams); |
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.
same
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.
changed
| ResourceCallback<CreateResult<T>>[] | ||
| ResourceCallback<CreateResult<T>>; |
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.
You should create a type for this union, to avoid repetition
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.
changed
* @param {U} params The params / result to pass to the callbacks | ||
* @returns {Promise<U>} The params / result after the callbacks have been applied | ||
*/ | ||
const applyCallbacks = async function <U>( |
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 suggest you extract that function, and unit test 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.
If I extract this function, I see two options :
- either we have to pass the handlers + dataProvider each time, the drawback is that it's quite verbose and the code is less clear :
newParams = await applyCallbacks(
dataProvider,
handlers,
resource,
'beforeGetList',
newParams
);
- Or I could bind the the repeated parameters :
const applyCallbacksWithContext = applyCallbacks.bind(
null,
dataProvider,
handlers
);
....
newParams = await applyCallbacksWithContext(
resource,
'beforeGetList',
newParams
);
I tend to prefer the first option because the code is less complex to understand even though it's very verbose.
What is your opinion on that ?
* The list of all possible data provider hooks | ||
* @see https://marmelab.com/react-admin/DataProviders.html#data-provider-hooks | ||
*/ | ||
export const dataProviderHooks = [ |
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.
Why do you export 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.
I use it in my own code to apply a callback for every possible hook (for login purpose essentially).
I understand this could be in my own code though.
Regarding the documentation, I hesitated between added the new features in the main part of the documentation, or add an "advanced use" section, and explain the new possibilities, and how they are very useful, in which cases, best practices, .... Can you tell me if the implementation is OK for you ? If yes, I will move on unit testing :) |
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.
Thanks!
Regarding the documentation, I hesitated between added the new features in the main part of the documentation, or add an "advanced use" section, and explain the new possibilities, and how they are very useful, in which cases, best practices, ....
I would probably add this in the Code Organization section.
Also add some details about execution order and the possibility to add the same resource multiple times (it was already possible, but not very clear for me in the documentation).
This is definitely worth adding in the Usage section
/** | ||
* The list of all possible data provider hooks | ||
* @see https://marmelab.com/react-admin/DataProviders.html#data-provider-hooks | ||
*/ | ||
export enum dataProviderHooks { | ||
afterCreate = 'afterCreate', | ||
afterDelete = 'afterDelete', | ||
afterDeleteMany = 'afterDeleteMany', | ||
afterGetList = 'afterGetList', | ||
afterGetMany = 'afterGetMany', | ||
afterGetManyReference = 'afterGetManyReference', | ||
afterGetOne = 'afterGetOne', | ||
afterUpdate = 'afterUpdate', | ||
afterUpdateMany = 'afterUpdateMany', | ||
beforeCreate = 'beforeCreate', | ||
beforeDelete = 'beforeDelete', | ||
beforeDeleteMany = 'beforeDeleteMany', | ||
beforeGetList = 'beforeGetList', | ||
beforeGetMany = 'beforeGetMany', | ||
beforeGetManyReference = 'beforeGetManyReference', | ||
beforeGetOne = 'beforeGetOne', | ||
beforeUpdate = 'beforeUpdate', | ||
beforeUpdateMany = 'beforeUpdateMany', | ||
beforeSave = 'beforeSave', | ||
afterRead = 'afterRead', | ||
afterSave = 'afterSave', | ||
} |
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.
As this is not used anywhere, I believe we should remove it
I have fixed + added some unit tests for the new features |
agreed, did that :) |
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.
Can you please remove the package-lock.json
file? Besides, I don't get why there are changes in the yarn.lock
file.
deleted + reverted :) |
Thanks! |
Problem
Using withLifecycleCallbacks when you have many callbacks can be very verbose and hard to read / maintain. This can be improved a lot with two features :
1. Support for a wildcard resource : *
Imagine for example your API returns "identifier" and you want to rename it to "id" to make it work with react-admin. You will need to create a callback for each resource. But now you can do that :
2. Support for arrays of callbacks
When you have a lot of callbacks / hooks (which is often the case when having a graphql API), you may want to have one callback per purpose, and name it after that purpose. So you could have many callbacks for the same resource / hook. This is already possible but quite verbose / difficult to read.
The idea is to allow to pass an array of callbacks, while still supporting passing directly one callback.
Currently :
After :