I have a custom button in which I use useMutation to add a related information. Upon success I use useRefresh to refresh the ReferenceManyField on the page that lists that additional information.
It works only the first time. I enter the additional information, click on the button, the useMutation succeeds, the refresh call is executed and the information gets updated on the screen.
However, if I provide new additional information again and click the button, the useMutation succeeds, no errors in Console, but the refresh does not get executed and the screen is not updated. All the following repeats do the same. The added information gets displayed only if I refresh the page in browser.
It used to work correctly in the past, I am not sure if there is some dependency module that I updated that caused it to stop working.
Am I missing something or is this a bug?
import React from 'react';
import Button from '@material-ui/core/Button';
import { useFormState } from 'react-final-form';
import { useMutation, useNotify, useRefresh, Loading } from 'react-admin';
const AddDeptAppButton = (formData, ...props) => {
const notify = useNotify();
const formState = useFormState();
const myrefresh = useRefresh();
const [approve, { loading }] = useMutation(
{
type: 'update',
resource: 'departments',
payload: { id: formState.initialValues.id, data: { deptID: formState.initialValues.id, deptAppID: formState.values._deptAppID, deptAppRightsID: formState.values._deptAppRightsID } }
},
{
onSuccess: ({ data }) => {
notify(`Department app #${data.id} added`);
myrefresh();
},
onFailure: (error) => {
notify(`Error: ${error.message}`, 'warning');
}
}
);
return <div style={{ flex: 1, marginBottom: 16 }} ><Button color="primary" variant="contained" onClick={approve} >Add app</Button></div>
};
export default (AddDeptAppButton);
In which page are you using this button? A standard react-admin Edit page?
Please use the simple example codesandbox to help us reproduce your bug.
It is in a standard Edit component. I cannot use codesandbox as it is a complex and it uses backend API in a private network.
Perhaps this can help. It is AddDeptAppButton in second TabPanel. The DeleteButton correctly refreshes the list, and I can use the DeleteButton multiple times in sequence, always refreshing correctly, but if I use Delete Button at least once, and then try the AddDeptAppButton , the AddDeptAppButton does not refresh even on the first click.
As I explained previously, if the first button I click is teh AddDeptAppButton, it will refresh the first time, but it will not refresh on the consequent clicks.
export const DeptEmployeeList = props => {
const ACFClasses = ACFDataGridClasses();
const classes = useStyles();
return <Tabs>
<TabList>
<Tab>Employees</Tab>
<Tab>Default Applications</Tab>
</TabList>
<TabPanel>
<List {...props} title={<PostTitle />} bulkActionButtons={false} filters={<EmployeeFilter />} pagination={<PostPagination />} >
<Datagrid rowClick="edit" classes={ACFClasses}>
<TextField source="id" className={classes.idColumn} />
<TextField source="LastName" />
<TextField source="FirstName" />
<EmailField source="Email" />
<TextField source="UserName" />
<TextField source="BadgeNumber" />
{/* <TextField source="EmpEmergencyNum" /> */}
<TextField source="EmpType" />
<TextField source="JobTitle" />
{/* <TextField source="PAProfile" /> */}
<TextField source="Status" />
</Datagrid>
</List>
</TabPanel>
<TabPanel>
<Edit resource="departments" id={deptID} key='id' basePath="" title={<PostTitle />} aside={<AsideApps />} {...props}>
<SimpleForm toolbar={null} >
<ReferenceManyField
addLabel={false}
reference="departmentapps"
target="deptID"
>
<Datagrid classes={ACFClasses}>
<TextField label="Application" source="AppName" style={{ whiteSpace: 'nowrap' }} sortable={false} />
<TextField label="Permission" source="AppRightsText" sortable={false} />
<DeleteButton redirect={false} />
</Datagrid>
</ReferenceManyField>
<></>
<FormDataConsumer>
{({ formData, ...rest }) =>
formData._deptAppID ?
<>
<div style={{ display: "flex", alignItems: "center" }}>
<ReferenceInput label="Application" source="_deptAppID" reference="deptappsavailable" perPage={300} formClassName={classes.inlineBlock}>
<SelectInput optionText="AppName" />
</ReferenceInput>
<SelectInput optionText="AppRightsText" source="_deptAppRightsID" choices={choices} optionValue="id" defaultValue="1" label="Permissions" formClassName={classes.inlineBlock} />
{(formData._deptAppID && formData._deptAppRightsID) ?
<AddDeptAppButton />
:
<></>}
</div>
</>
:
<>
<ReferenceInput label="Application" source="_deptAppID" reference="deptappsavailable" perPage={300} formClassName={classes.inlineBlock}>
<SelectInput optionText="AppName" />
</ReferenceInput>
</>
}
</FormDataConsumer>
</SimpleForm>
</Edit>
</TabPanel>
</Tabs>
};
useRefresh() works by incrementing a version value that is stored in the Redux state. All page controllers (useListController, useEditController) include this version in the cache key they use to call the dataProvider. So when useRefresh() executes, the controllers are forced to refetch the dataProvider:
// extract from useEditController
const refresh = useRefresh();
const version = useVersion();
const { data: record, loading, loaded } = useGetOne(resource, id, {
version, // used to force reload
action: CRUD_GET_ONE,
onFailure: () => {
refresh();
},
});
This works fine in conventional layouts. You're using a non-standard layout, so this is not something we support. We won't investigate further on your case - I suggest you ask on StackOverflow.