React i18next languageChanged 事件被多次调用

发布于 2025-01-14 01:25:01 字数 8925 浏览 1 评论 0原文

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

问题说明

  1. i18Next 函数的

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

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

i18Next 配置

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

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.

Problem Explanation

  1. 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 ?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

泛泛之交 2025-01-21 01:25:01

我怀疑问题是:

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

每次组件渲染时,它都会再次订阅 languageChanged 事件...

尝试将其移动到 useEffect 中,如下所示:

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

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

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]);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文