When I use React-Admin I can't see any change on the list until I manually hard reload it. This is very confusing because the new record is getting a success response or the update record but the getList or List component is still fetching data from the cache disk this mean it's not fetching again the API.
I have configure my Nginx image to avoid caching just for test purpose, I have tried to use the react-queries method : queryClient.invalidateQueries() and many other things but I really don't find out how to do.
Here is two simple components where I have this issue :
- OfficeList.tsx :
export const OfficeList = () => {
const refresh = useRefresh();
const location = useLocation();
const matchCreate = matchPath({
path: '/offices/create',
end: true
}, location.pathname);
const matchUpdate = matchPath({
path: '/offices/:id',
end: false
}, location.pathname);
const isCreateOpen = !!matchCreate;
const isEditOpen = !!matchUpdate && location.pathname !== '/offices/create';
const recordId = isEditOpen ? matchUpdate?.params.id : null;
return (
<>
<List
sort={{ field: "login", order: "ASC" }}
perPage={10}
actions={<OfficeActions/>}
sx={{ marginTop: '30px', marginRight: '10px' }}
>
<Datagrid bulkActionButtons={false} onSubmit={refresh}>
<TextField source="name" label={'Nom'}/>
<EmailField source="email" />
<TextField label="Adresse" source="address" />
<NumberField label="Dentistes" source="users.length" textAlign="center" sx={{fontSize: '1.1em', fontWeight: '500'}}/>
<TextField source="phoneNumber" fontStyle={'italic'} />
<TextField source="city" />
<DateField label="Date MàJ" source="updatedAt" textAlign="center" fontStyle={'italic'}/>
<CustomActionsField />
</Datagrid>
</List>
{ isCreateOpen && <OfficeCreate open={isCreateOpen} /> }
{ isEditOpen && <OfficeEdit open={isEditOpen} recordId={recordId} /> }
</>
);
};
- OfficeCreate.tsx component :
interface OfficeCreateProps {
open: boolean;
}
export const OfficeCreate = ({ open } : OfficeCreateProps) => {
const notify = useNotify();
const refresh = useRefresh();
const redirect = useRedirect();
const handleClose = () => redirect('/offices');
const onSuccess = () => {
notify(`Le cabinet a été créé avec succès.`, {type: 'success'});
redirect('/offices');
refresh();
};
const onError = () => {
notify(`Une erreur est survenue, vérifiez les données.`, {type: 'error'});
};
const validateName = [required(), minLength(3, 'Nom: 3 caractères minimum.'), maxLength(50, 'Nom: 50 caractères maximum.')];
const validateEmail = email('Email: Format non valide.');
const validatePhoneNumber = regex(/^((\+|00)33\s?|0)[3467](\s?\d{2}){4}$/, 'Téléphone: Format non valide commencez par 04, 06 ou 07 suivis de de 8 chiffres.');
const validateZipCode = regex(/^(?:0[1-9]|[1-9]\d|9[0-8])\d{3}$/, 'Code postal: 2 chiffres minimum et 5 chiffres maximum.');
return (
<Dialog open={open} onClose={handleClose} >
<DialogTitle>Créer un cabinet :</DialogTitle>
<Create <Office>
mutationOptions={{onSuccess, onError}}
resource="offices"
title=" "
sx={{ width: 500, '& .RaCreate-main': { mt: 0 } }}
>
<SimpleForm toolbar={<SaveButton label="Enregistrer" sx={{ margin: 2 , borderRadius: '40px'}}/>}>
<TextInput
label='Nom'
source="name"
validate={validateName}
fullWidth
/>
<TextInput
label='Email'
source="email"
validate={validateEmail}
fullWidth
/>
<TextInput
label='Adresse'
source="address"
fullWidth
/>
<Box display={{ xs: 'block', sm: 'flex', width: '100%' }}>
<Box flex={1} mr={{ xs: 0, sm: '0.5em' }}>
<TextInput
label='Téléphone'
source="phone"
validate={validatePhoneNumber}
fullWidth
/>
</Box>
<Box flex={1} mr={{ xs: 0, sm: '0.5em' }}>
<TextInput
label='Ville'
source="city"
fullWidth
/>
</Box>
</Box>
<TextInput
label='Code postal'
source="zipcode"
validate={validateZipCode}
/>
</SimpleForm>
</Create>
</Dialog>
);
};
And finally here is the getList method I use in my dataProvider and the httpClient method :
const httpClient = (url: string, options: RequestInit = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/ld+json' }) as Headers;
}
if (!(options.body instanceof FormData)) {
options.headers = new Headers({ 'Content-Type': 'application/json'}) as Headers;
}
const token = localStorage.getItem('token');
if (token) {
(options.headers as Headers).set('Authorization', `Bearer ${token}`);
}
return fetchUtils.fetchJson(url, options);
};
getList: async (
resource: string,
params: {
pagination: { page: any; perPage: any; };
sort: { field: any; order: any; };
filter: any; }
) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
let query = {
...fetchUtils.flattenObject(params.filter),
page: page,
itemsPerPage: perPage
};
if (order) {
query[`order[${field}]`] = order.toLowerCase();
}
const url = `${import.meta.env.VITE_API_BASE}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ json }) => {
if (!json['hydra:member']) {
throw new Error('No hydra:member in response');
}
return {
data: json['hydra:member'],
total: json['hydra:totalItems'],
};
});
},
I had to update a little bit the way it work because I'm using Symfony / Api Platform on the back end so the dataProvider is not made for json+ld responses.
I don't know what is the best way to do it but I need to see the list being updated after a CREATE or an UPDATE request !