React-Admin cache getList from cache disk even if there is an update or a new record

25 Views Asked by At

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 :

  1. 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} /> }

        </>
    );
};
  1. 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 !

0

There are 0 best solutions below