Reputation: 121
I have used i18next for translation in my react js application. I have added a language dropdown in Header.jsx file which is common for all the pages. I'm fetching data based on the current language in my one of the pages quiz.jsx. So on language change api should be called again.
languageChanged() event of i18Next function calls multiple times on language dropdown change. It should be called only one time. I don't know why this function is being called multiple time ?
I want to implement languageChanged() event of i18Next only on one page but currently it is calling on each and every page. So api is fetching data in all the pages which is unnecessary data for that page.
i18Next Configuration
language.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import config from './config';
var resources = {};
//Dynamically reading languages from config file
config.supportedLanguages.forEach(element => {
resources[element] = {
translations: require('../locale/' + element + '.json')
};
});
i18n.use(initReactI18next).init({
fallbackLng: config.defaultLanguage,
lng: config.defaultLanguage,
resources,
ns: ['translations'],
defaultNS: 'translations',
debug:true
});
i18n.languages = config.supportedLanguages;
export default i18n;
Header.jsx
import React, { useState, useEffect } from 'react';
import { Dropdown, DropdownButton } from 'react-bootstrap';
import { withTranslation, useTranslation } from 'react-i18next';
import * as api from "../../utils/api";
const TopHeader = ({ t }) => {
const { i18n } = useTranslation();
// language selector
const [languageValue, setLanguageValue] = useState('')
const [languages, setLanguages] = useState('');
const languageChange = (data) => {
setLanguageValue(data.language)
i18n.changeLanguage(data.code);
localStorage.setItem('language', JSON.stringify(data));
}
const getUserSelectedLanguage = () => {
var user_selected_lang = localStorage.getItem('language');
if (user_selected_lang && user_selected_lang !== undefined) {
user_selected_lang = JSON.parse(user_selected_lang);
}
return user_selected_lang;
}
//api render
useEffect(() => {
api.getLanguages().then((response) => {
if (!response.error) {
setLanguages(response.data);
var user_selected_lang = getUserSelectedLanguage();
if (user_selected_lang) {
selectUserLanguage(user_selected_lang);
} else {
var index = response.data.filter((data) => {
return data.code === config.defaultLanguage;
})
selectUserLanguage(index[0]);
}
}
});
}, []);
return (
<React.Fragment>
<div className="small__top__header">
<div className="row justify-content-between align-items-center">
<div className="col-md-6 col-12">
<div className="dropdown__language">
<DropdownButton className="inner-language__dropdown" title={languageValue ? languageValue : "Select Language"}>
{languages && languages.map((data, key) => {
return (
<Dropdown.Item onClick={() => languageChange(data)} value={languageValue} id={data.id} active={languageValue === data.language ? "active" : ""} key={data.language}>{data.language}</Dropdown.Item>
)
})}
</DropdownButton>
</div>
</div>
</div>
</div>
</React.Fragment>
)
}
export default withTranslation()(TopHeader);
quiz.jsx
import React, { useState, useEffect } from 'react';
import { withTranslation, useTranslation } from 'react-i18next';
import { Spinner } from 'react-bootstrap';
const Quiz = ({ t }) => {
const [category, setCategory] = useState({ all: '', selected: '' });
const [subCategory, setsubCategory] = useState({ all: '', selected: '' });
const [level, setLevel] = useState([]);
const { i18n } = useTranslation();
useEffect(() => {
getAllData();
}, []);
i18n.on('languageChanged', () => {
getAllData();
});
const getAllData = () => {
// This function will call the Category , Subcategory and Level API to fetch the data
// And set the local states
}
return (
<React.Fragment>
<Header />
<div className="quizplay mb-5">
<div className="container">
<div className="row morphisam mb-5">
<div className="col-xxl-3 col-xl-4 col-lg-4 col-md-12 col-12">
<div className="left-sec">
{/* left category sec*/}
<div className="bottom__left">
<div className="cat__Box">
<span className="left-line"></span>
<h3 className="quizplay__title text-uppercase text-white font-weight-bold">{t('Categories')}</h3>
<span className="right-line"></span>
</div>
<div className="bottom__cat__box">
<ul className="inner__Cat__box">
{
category.all ? category.all.map((data, key) => {
return (
<li className='d-flex' key={key} onClick={() => handleChangeCategory(data)}>
<div className={`w-100 button ${category.selected && category.selected.id === data.id ? "active-one" : "unactive-one"}`}>
<span className="Box__icon">
<img src={data.image} alt="" />
</span>
<p className="Box__text">{data.category_name}</p>
</div>
</li>
)
})
:
<div className='text-center'>
<Spinner animation="border" role="status"></Spinner>
</div>
}
</ul>
</div>
</div>
</div>
</div>
{/* sub category middle sec */}
<div className="col-xxl-9 col-xl-8 col-lg-8 col-md-12 col-12">
<div className="right-sec">
<SubCatslider data={subCategory.all} selected={subCategory.selected} onClick={handleChangeSubCategory} />
</div>
<div className="right__bottom cat__Box mt-4">
<span className="left-line"></span>
<h6 className="quizplay__title text-uppercase text-white font-weight-bold">{t('levels')}</h6>
<span className="right-line"></span>
</div>
{/* levels sec */}
<div className="row level-row">
<UnlockLevel count={level.count} category={category.selected} subcategory={subCategory.selected} unlockedLevel={level.unlockedLevel} />
</div>
</div>
</div>
</div>
</div>
</React.Fragment>
)
}
export default withTranslation()(Quiz);
Upvotes: 3
Views: 5974
Reputation: 3168
I suspect the problem is:
i18n.on('languageChanged', () => {
getAllData();
});
Each time the component renders it will subscribe again for the languageChanged event...
Try to move it in the useEffect, something like:
const handleLanguageChanged = useCallback(() => {
getAllData();
}, []);
useEffect(() => {
i18n.on('languageChanged', handleLanguageChanged);
return () => {
i18n.off('languageChanged', handleLanguageChanged);
};
}, [handleLanguageChanged]);
Upvotes: 6