多次调用 React i18next languageChanged 事件

React i18next languageChanged event called multiple times

我在我的 React JS 应用程序中使用 i18next 进行翻译。我在 Header.jsx 文件中添加了一个语言下拉列表,这对所有页面都是通用的。我正在根据我的其中一个页面 quiz.jsx 中的当前语言获取数据。所以在语言更改时 api 应该再次调用。

问题说明

  1. languageChanged() i18Next 函数的事件在语言下拉列表更改时多次调用。它应该只被调用一次。我不知道为什么这个函数被多次调用?

  2. 我只想在一页上实现 i18Next 的 languageChanged() 事件,但目前它正在每一页上调用。所以 api 正在获取所有页面中的数据,这对于该页面来说是不必要的数据。

i18下一个配置

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);

我怀疑问题是:

i18n.on('languageChanged', () => {
    getAllData();
});

每次组件呈现时,它都会再次订阅 languageChanged 事件...

尝试在useEffect中移动它,比如:

const handleLanguageChanged = useCallback(() => {
    getAllData();
}, []);

useEffect(() => {
    i18n.on('languageChanged', handleLanguageChanged);
    return () => {
        i18n.off('languageChanged', handleLanguageChanged);
    };
}, [handleLanguageChanged]);