conscious-sapphire
conscious-sapphireβ€’2y ago

make a new call api after the x-locale change

Hello i'm currently making an app with refine and i have implement i18n. I also have an API which allow me to fetch data in french and in english accord to "x-locale" in REST request header. I would like to know how can i make a new api call without refreshing the page ?
52 Replies
absent-sapphire
absent-sapphireβ€’2y ago
Hi @harisris, All the data related hooks returns refetch functionality. for example;
const { tableProps, tableQueryResult } = useTable<IPost>();
tableQueryResult.refetch();
const { tableProps, tableQueryResult } = useTable<IPost>();
tableQueryResult.refetch();
u can call this tableQueryResult.refetch()
conscious-sapphire
conscious-sapphireβ€’2y ago
Hi Alican, thank you for your response !! I am using a DataGrid component and i use a useDataGrid hook. I do not find any refetch function for this hook. How can I make it work with useDataGrid or does any other solution exist ?
absent-sapphire
absent-sapphireβ€’2y ago
it's inside tableQueryResult object.
const { tableProps, tableQueryResult } = useDataGrid();
tableQueryResult.refetch();
const { tableProps, tableQueryResult } = useDataGrid();
tableQueryResult.refetch();
conscious-sapphire
conscious-sapphireβ€’2y ago
Finnaly this does the refetch but the request has the same header. I put the tableQueryResult.refetch(); inside a useEffect triggered when the locale change and nothing change. Does this functionnality just replay the last request ?
absent-sapphire
absent-sapphireβ€’2y ago
Yes, It's refetching with current values. I tested this code snippet. it's work as expected. what is your implementation look like ?
const [xLocale, setXLocale] = useState("locale-1");

const { tableProps, tableQueryResult } = useDataGrid({
metaData: {
headers: { "x-locale": xLocale },
},
});

useEffect(() => {
tableQueryResult.refetch();
}, [xLocale]);
const [xLocale, setXLocale] = useState("locale-1");

const { tableProps, tableQueryResult } = useDataGrid({
metaData: {
headers: { "x-locale": xLocale },
},
});

useEffect(() => {
tableQueryResult.refetch();
}, [xLocale]);
conscious-sapphire
conscious-sapphireβ€’2y ago
const currentLanguage = i18n.language;
const {dataGridProps, tableQueryResult} = useDataGrid<IDomains>({
hasPagination: false,
});
useEffect(() => {
tableQueryResult.refetch();
}, [currentLanguage]);
const currentLanguage = i18n.language;
const {dataGridProps, tableQueryResult} = useDataGrid<IDomains>({
hasPagination: false,
});
useEffect(() => {
tableQueryResult.refetch();
}, [currentLanguage]);
i will try your code
absent-sapphire
absent-sapphireβ€’2y ago
where do you set headers ?
conscious-sapphire
conscious-sapphireβ€’2y ago
the headers are set in an other component but the set work fine when i refresh the page the request is made with the right header
export const LanguageSelector: React.FC = () => {
const {i18n} = useTranslation();
const changeLanguage = useSetLocale();
const locale = useGetLocale();
const currentLocale = locale();
/**
* return the current language
* @param lang current language
*/
const selectedLanguage = (lang: string) => {
if (lang === "fr-FR") return "https://flagcdn.com/fr.svg";
else if (lang === "en") return "https://flagcdn.com/gb.svg";
};
return (
<>
<TextField
select
value={currentLocale}
onChange={(e) => {
changeLanguage(e.target.value);
}}
className="language-selector"
size="small"
>
{i18n.languages.map((lang) => (
<MenuItem key={lang} value={lang}>
<span className="language-selector-image">
<img
alt="flag"
src={selectedLanguage(lang)}
loading="lazy"
width="30"
/>
<p>{lang === "en" ? "English" : "Français"}</p>
</span>
</MenuItem>
))}
</TextField>
</>
);
};
export const LanguageSelector: React.FC = () => {
const {i18n} = useTranslation();
const changeLanguage = useSetLocale();
const locale = useGetLocale();
const currentLocale = locale();
/**
* return the current language
* @param lang current language
*/
const selectedLanguage = (lang: string) => {
if (lang === "fr-FR") return "https://flagcdn.com/fr.svg";
else if (lang === "en") return "https://flagcdn.com/gb.svg";
};
return (
<>
<TextField
select
value={currentLocale}
onChange={(e) => {
changeLanguage(e.target.value);
}}
className="language-selector"
size="small"
>
{i18n.languages.map((lang) => (
<MenuItem key={lang} value={lang}>
<span className="language-selector-image">
<img
alt="flag"
src={selectedLanguage(lang)}
loading="lazy"
width="30"
/>
<p>{lang === "en" ? "English" : "Français"}</p>
</span>
</MenuItem>
))}
</TextField>
</>
);
};
here is the component which change the locale
absent-sapphire
absent-sapphireβ€’2y ago
I undestand. refine uses react-query under the hood. and actually headers out of scope for react-query. we need to use fetch library with react-query like axios or window.fetch our examples uses react query with axios. and we have implementation for passing headers with data hooks. for example: useDataGrid takes metaData, and passing to react-query and react-query passes to axios. in this situtation. you need to write this useEffect and metaData to everywhere. actually it's a bad implementation let me think what we can do. i have couple ideas https://refine.dev/docs/api-reference/core/hooks/invalidate/useInvalidate/ with this, you can invalidate all queries. you don't need to useEffect and refetch. I thought you would only use it once.
invalidate({
dataProviderName: "your-data-provider-name",
invalidates: ["all"],
});
invalidate({
dataProviderName: "your-data-provider-name",
invalidates: ["all"],
});
you can do that after language changed
conscious-sapphire
conscious-sapphireβ€’2y ago
Ok i'm going to try
absent-sapphire
absent-sapphireβ€’2y ago
but before do that, we need to set x-locale header are you using axios with custom data provider?
conscious-sapphire
conscious-sapphireβ€’2y ago
ok ok i try and i get back to you yes i'm using axios
absent-sapphire
absent-sapphireβ€’2y ago
we have couple of options to do that. you can search on google like "how to set dynamically axios headers" but this is the easiest way for me. you can get xLocale before each request.
axiosInstance.interceptors.request.use(config => {
config.headers.put['x-locale'] = localStorage.getItem('x-locale');
return config;
});
axiosInstance.interceptors.request.use(config => {
config.headers.put['x-locale'] = localStorage.getItem('x-locale');
return config;
});
but dont forget. you need to call invalidate after modify the localeStorage
conscious-sapphire
conscious-sapphireβ€’2y ago
actually the language is set correctly in the localStorage and it change when i change it with the select component
absent-sapphire
absent-sapphireβ€’2y ago
nice, but we need to update default axios headers after you change locale or you can set x-locale header before every request with axios interceptors
conscious-sapphire
conscious-sapphireβ€’2y ago
this is also already done and it works i'm trying to implement the invalidate functionnality I tried to put your code snippet in the component where is the select onChange, just after i modify the local but nothing change. The invalidate is not trigger and there is no new API call
absent-sapphire
absent-sapphireβ€’2y ago
Hi again, sorry for your trouble. i quickly check invalidate() function in our examples. it's work as espected. can you share latest state of your implementation with me ? i will try to reproduce
conscious-sapphire
conscious-sapphireβ€’2y ago
Hi excuse me to bother your since this morning πŸ˜…
absent-sapphire
absent-sapphireβ€’2y ago
no no, i'm glad to help if i canπŸ™
conscious-sapphire
conscious-sapphireβ€’2y ago
<TextField
select
value={currentLocale}
onChange={(e) => {
changeLanguage(e.target.value);
invalidate({
dataProviderName: "dataProvider",
invalidates: ["all"],
});
}}
className="language-selector"
size="small"
>
{/*MENU ITEMS*/}
))}
</TextField>
<TextField
select
value={currentLocale}
onChange={(e) => {
changeLanguage(e.target.value);
invalidate({
dataProviderName: "dataProvider",
invalidates: ["all"],
});
}}
className="language-selector"
size="small"
>
{/*MENU ITEMS*/}
))}
</TextField>
this in the component which change the language
absent-sapphire
absent-sapphireβ€’2y ago
dataProviderName: "dataProvider", can you remove this and try again ? you have only one data provider isn't it ?
conscious-sapphire
conscious-sapphireβ€’2y ago
yes only one which i use in App.tsx
absent-sapphire
absent-sapphireβ€’2y ago
sorry for misunderstanding. I should have explained better. if you have one. it will be probably work when you delete this dataProviderName: "dataProvider"
conscious-sapphire
conscious-sapphireβ€’2y ago
this time it trigger and a new api call was emit but the data grid do not change language
absent-sapphire
absent-sapphireβ€’2y ago
on chrome network tab, did you see header with new value on x-locale ?
conscious-sapphire
conscious-sapphireβ€’2y ago
yes it's set to fr-FR for exampleand do not change even if the select is in english but if i reload the page the content is now in english
absent-sapphire
absent-sapphireβ€’2y ago
hmm. please let me go over the steps again. i want to understand better select en, then you see on network tab header is setted to english select fr-FR, then you see network tab header is setted to fr-FR but nothing is changed on UI. im i correct?
conscious-sapphire
conscious-sapphireβ€’2y ago
select en, then you see on network tab header is setted to english select fr-FR, then you see network tab header is setted to english
absent-sapphire
absent-sapphireβ€’2y ago
can you try this ?
onChange={async (e) => {
await changeLanguage(e.target.value);
invalidate({
dataProviderName: "dataProvider",
invalidates: ["all"],
});
}}
onChange={async (e) => {
await changeLanguage(e.target.value);
invalidate({
dataProviderName: "dataProvider",
invalidates: ["all"],
});
}}
conscious-sapphire
conscious-sapphireβ€’2y ago
i've try this and this does not work
absent-sapphire
absent-sapphireβ€’2y ago
Sorry Haris, i couldn't find to issue. can you reproduce the issue on stackblitz or can you provide me github repo ? i can try to debug.
conscious-sapphire
conscious-sapphireβ€’2y ago
it is a private repo and a private api with a authentication token i don't if it will work on stackblitz i'll try
absent-sapphire
absent-sapphireβ€’2y ago
please can you share your axios interceptor code with me? hmm. please let me go over the steps again πŸ˜„ select en, then you see on localStorage i18nextLng="en" select fr-FR, then you see localStorage i18nextLng="fr-FR" ofc i18nextLng key name can be change by implementation
conscious-sapphire
conscious-sapphireβ€’2y ago
select en, then you see on localStorage i18nextLng="en" select fr-FR, then you see localStorage i18nextLng="en"
absent-sapphire
absent-sapphireβ€’2y ago
what is your i18n provider ?
conscious-sapphire
conscious-sapphireβ€’2y ago
const i18nProvider = {
translate: (key: string, params: object) => t(key, params),
changeLocale: (lang: string) => i18n.changeLanguage(lang),
getLocale: () => i18n.language,
};
const i18nProvider = {
translate: (key: string, params: object) => t(key, params),
changeLocale: (lang: string) => i18n.changeLanguage(lang),
getLocale: () => i18n.language,
};
absent-sapphire
absent-sapphireβ€’2y ago
sorry i was thinkink your i18n provider is using local storage for saving language. but probably using something else. because this is stays same
select en, then you see on localStorage i18nextLng="en"
select fr-FR, then you see localStorage i18nextLng="en"
select en, then you see on localStorage i18nextLng="en"
select fr-FR, then you see localStorage i18nextLng="en"
according to this : https://github.com/i18next/i18next-browser-languageDetector#detector-options it uses query string btw. this probably will work. but it's not a best practive.
onChange={async (e) => {
await changeLanguage(e.target.value);
localStorage.setItem("i18nextLng", e.target.value)
invalidate({
dataProviderName: "dataProvider",
invalidates: ["all"],
});
}}
onChange={async (e) => {
await changeLanguage(e.target.value);
localStorage.setItem("i18nextLng", e.target.value)
invalidate({
dataProviderName: "dataProvider",
invalidates: ["all"],
});
}}
with someway you need to pass current language value to axios. axiosInstance.interceptors.request.use(config => { config.headers.put['x-locale'] = <current-value-from-i18n-provider> return config; });
conscious-sapphire
conscious-sapphireβ€’2y ago
currently this is how i set the x-locale in axios header
import axios from "axios";

export const axiosInstance = axios.create({
headers: {
"Content-Type": "application/merge-patch+json",
Accept: "application/json",
"x-locale": localStorage.getItem("i18nextLng") || "",
},
});
import axios from "axios";

export const axiosInstance = axios.create({
headers: {
"Content-Type": "application/merge-patch+json",
Accept: "application/json",
"x-locale": localStorage.getItem("i18nextLng") || "",
},
});
absent-sapphire
absent-sapphireβ€’2y ago
this is the default header. this will run once when axios initalized you need this too
axiosInstance.interceptors.request.use(config => {
config.headers.put['x-locale'] = localStorage.getItem("i18nextLng") || "",
return config;
});
axiosInstance.interceptors.request.use(config => {
config.headers.put['x-locale'] = localStorage.getItem("i18nextLng") || "",
return config;
});
but if localStorage stays same. this will not work
conscious-sapphire
conscious-sapphireβ€’2y ago
ok that is why the localStorage not change
absent-sapphire
absent-sapphireβ€’2y ago
why
conscious-sapphire
conscious-sapphireβ€’2y ago
sorry it was a question and localStorage do not change event if i put your code snippet in the selet' onChange()
absent-sapphire
absent-sapphireβ€’2y ago
sometimes chrome not refresh automatically you need to press this
No description
conscious-sapphire
conscious-sapphireβ€’2y ago
sorry not the localStorage but the x-locale in the request header the localStorage work fine
absent-sapphire
absent-sapphireβ€’2y ago
Harris im so sorry. okay, i cloned this https://refine.dev/docs/api-reference/core/providers/i18n-provider/#example-1 and try to implement. i only add this and it works !!!
onClick={() => {
changeLanguage(lang)
invalidate({
invalidates: ['all'],
})
}}
onClick={() => {
changeLanguage(lang)
invalidate({
invalidates: ['all'],
})
}}
axiosInstance.interceptors.request.use(
(config) => {
// @ts-ignore
config.headers['x-locale'] = localStorage.getItem('i18nextLng') || ''
return config
},
(error) => {
return Promise.reject(error)
}
)
axiosInstance.interceptors.request.use(
(config) => {
// @ts-ignore
config.headers['x-locale'] = localStorage.getItem('i18nextLng') || ''
return config
},
(error) => {
return Promise.reject(error)
}
)
can you change your previous interceptors with above please πŸ™
conscious-sapphire
conscious-sapphireβ€’2y ago
It finnaly works !!!
absent-sapphire
absent-sapphireβ€’2y ago
YESSS im so sorry. i share wrong code with you probably axios api changed thank you for time 😦
conscious-sapphire
conscious-sapphireβ€’2y ago
Your a boss πŸ’ͺ
absent-sapphire
absent-sapphireβ€’2y ago
you are the boss πŸš€
conscious-sapphire
conscious-sapphireβ€’2y ago
thanks YOU for your time sorry that i have bother your since this morning have a nice day
absent-sapphire
absent-sapphireβ€’2y ago
no no. im glad we solve the problem ! please don't be hesitate to ask questions πŸ™ have a nice day too
conscious-sapphire
conscious-sapphireβ€’2y ago
thank you πŸ‘