Data Provider 'update' function: params.data has updated ID but also carries old body JSON fields
See original GitHub issueWhat you were expecting:
I have two resources Article and Author and an Article can have one single Author field with name author.
My expectation is that when I change the value of the author field in an Article during Edit, the submission of an Article update should trigger a call to my data provider’s update function passing in a JSON of Article that simply updates the ID of the nested author field, i.e. something that looks like:
{
"id":2,
"title":"Some Title",
"url":"https://some.url.com",
"imageUrl":"https://some.image.url.com",
"visible":true,
"startVisible":"2019-09-29T11:11:00Z",
"endVisible":"2019-10-29T07:02:00Z",
"author":{
"id":6
}
}
What happened instead:
Contrary to my expectation, when I change the value of the author field in an Article during Edit, the submission of an Article update actually triggered a call to my data provider’s update function passing in a JSON of Article that embeds the complete JSON of the nested author field, i.e. something that looks like:
{
"id":2,
"title":"Some Title",
"url":"https://some.url.com",
"imageUrl":"https://some.image.url.com",
"visible":true,
"startVisible":"2019-09-29T11:11:00Z",
"endVisible":"2019-10-29T07:02:00Z",
"author":{
"id":6,
"blogSubTitle":"Old Blog Subtitle",
"blogTitle":"Old Blog Title",
"name":"Old Author Name1",
"introduction":"Old Intro",
"imageUrl":"https://old.image.url.com"
}
}
Steps to reproduce:
In my Edit for Article I have:
export const ArticleEdit = props => (
<Edit {...props}>
<TabbedForm>
<FormTab label="summary">
<TextInput disabled source="id"/>
<TextInput source="title" />
<TextInput source="url" />
<TextInput source="imageUrl" />
<ReferenceInput source="author.id" reference="author" label="resources.feed/article.fields.author">
<SelectInput optionText="name" />
</ReferenceInput>
</FormTab>
<FormTab label="campaign">
<DateInput source="startVisible" parse={dateParser}/>
<DateInput source="endVisible" parse={dateParser}/>
<BooleanInput source="visible"/>
</FormTab>
</TabbedForm>
</Edit>
);
At first glance, everything seems fine in that when I load the Article Edit page, I see the right REST API Data Provider functions (getList and getMany) get called for Author and thereafter the proper value show up for the author field, using the nameattribute ofAuthor`.
And when I choose a different Author in the drop down, the right REST API Data Provider calls get made for getMany too.
But when I click on the SAVE button, and having printed out the the params.data in my custom data provider, I see an unexpected JSON body as mentioned above.
Other information:
Environment
- React-admin version: 3.5.3
- Last version that did not exhibit the issue (if applicable):
- React version: 16.13.1
- Browser: Chrome / Edge
- Stack trace (in case of a JS error):
Issue Analytics
- State:
- Created 3 years ago
- Comments:7 (3 by maintainers)

Top Related StackOverflow Question
Thanks @fzaninotto for your comment. I understand and agree with your position with respect to keeping the centralized local store as a reason to keep these performance improvements and optimistic rendering. In fact, I was not actually suggesting that this store be removed though. My point was that even though I can do some pre-processing in my
dataProvider’supdate()implementation forarticle, as you correctly pointed out, it would not be architecturally as elegant because I would need to do this not only for articles / authors but also other entities down the road that may have such an embedding relationship.It boils down to whether we see this pre-processing as generic enough to generalize in my
dataProvider’supdatemethod (for all my entities), or more generic than that to push this into the react-admin pre-processing. I guess the answer is that it’s generic enough to put in mydataProviderbut not generic enough to put in the calling code of react-admin.I understand and appreciate that decision and know that I might have some more work cut out for me to make this work. But since I have control over my Grails REST controller I can devise something on that end to reduce work on the react-admin usage end. What I actually ended up deciding was to introduce a URL parameter that makes it render shallow objects like the way react-admin expects and in my
dataProvider’sgetOnecall, I post-pend my URL with this URL parameter. Not the most elegant either but beats having to process JSON in JS code and remove select fields before updating.Thanks again for looking into this and for the explanation on the local store.
React-admin keeps a local store of all the records your app has requested. These records are shared between views, and requests. So yes, if the
getOne()call returned an embedded record, theupdate()record will contain it, to.That’s a design choice, and that’s what allows us to provide significant performance improvements and optimistic rendering. This centralized store cannot be disabled.
In your case, you probably need to tweak the
update()method in yourdataProviderto remove the embedded record if the resource isarticle.Anyway, as this is not a react-admin bug, I’m closing the issue.