Как можно обновлять дочерние элементы, которые зависят от значения функции?

Здравствуйте! У меня есть хук, который возвращает переводы для компонентов на React. Вот его код: 

```javascript
import en from '../../locales/en.json'
import ru from '../../locales/ru.json'

const translations = {
	en,
	ru,
}

export type LanguageCode = 'en' | 'ru'
export const useTranslation = (initialLanguage: LanguageCode = 'en') => {
	const [language, setLanguage] = useState<LanguageCode>(initialLanguage)
	const [texts, setTexts] = useState<(typeof translations)[LanguageCode]>(
		translations[initialLanguage]
	)

	useEffect(() => {
		setTexts(translations[language])
	}, [language])

	const t = useCallback(
		(key: string): any => {
			const value = key.split('.').reduce((obj, keyPart) => {
				if (obj && typeof obj === 'object') {
					return obj[keyPart]
				}
				return undefined
			}, texts as Record<string, any>)
			//@ts-ignore
			return value
		},
		[language]
	)

	return { t, setLanguage, language }
}
```

Однако у меня возникла проблема: при изменении языка переводы не обновляются. Переводы извлекаются с помощью функции `t(key)`. Как я могу обновлять переводы для этих компонентов? 

```javascript
interface Props {}
const Component: FC<Props> = ({}) => {
	const { t, setLanguage } = useTranslation()

	return (
		<>
			<div>{t('helloWorld')}</div>
			<button
				onClick={() => {
					setLanguage('ru')
				}}
			>
				{t('helloWorld')}
			</button>
		</>
	)
}

export { Component }
```
  • 10 декабря 2024 г. 20:43
Ответы на вопрос 3
Ваша проблема связана с тем, что функция `t(key)` кэширует текст на основе значения `language`, но это значение не обновляется при изменении языка. Когда `setLanguage` вызывается и язык меняется, `texts` обновляется, но функция `t(key)` не пересчитывает значение, потому что ее зависимость задана на `language`, а не на `texts`.

Чтобы решить эту проблему, вам нужно добавить `texts` в зависимости `useCallback`, чтобы функция `t` перегенерировалась всякий раз, когда изменяется язык. Таким образом, `t(key)` будет использовать обновлённые переводы из `texts`.

Вот как может выглядеть исправленный код:

```javascript
import { useCallback, useEffect, useState } from 'react';
import en from '../../locales/en.json';
import ru from '../../locales/ru.json';

const translations = {
  en,
  ru,
};

export type LanguageCode = 'en' | 'ru';

export const useTranslation = (initialLanguage: LanguageCode = 'en') => {
  const [language, setLanguage] = useState<LanguageCode>(initialLanguage);
  const [texts, setTexts] = useState<(typeof translations)[LanguageCode]>(
    translations[initialLanguage]
  );

  useEffect(() => {
    setTexts(translations[language]);
  }, [language]);

  const t = useCallback(
    (key: string): any => {
      const value = key.split('.').reduce((obj, keyPart) => {
        if (obj && typeof obj === 'object') {
          return obj[keyPart];
        }
        return undefined;
      }, texts as Record<string, any>);
      return value; // Убрали @ts-ignore
    },
    [texts] // Изменили зависимость на texts
  );

  return { t, setLanguage, language };
};
```

Кроме того, в вашем компоненте `Component` вы сможете использовать функцию `t` для получения перевода, не повторяя нужный текст после нажатия кнопки. Обновленный код компонента будет выглядеть так:

```javascript
import React, { FC } from 'react';
import { useTranslation } from './path/to/useTranslation';

interface Props {}

const Component: FC<Props> = () => {
  const { t, setLanguage } = useTranslation();

  return (
    <>
      <div>{t('helloWorld')}</div>
      <button
        onClick={() => {
          setLanguage('ru');
        }}
      >
        {t('helloWorld')}
      </button>
    </>
  );
};

export { Component };
```

Теперь, когда вы изменяете язык, текст на кнопке будет обновляться в соответствии с новыми переводами.
У вас бак в хуке следующий: 
в useEffect вы реагируете на смену языка, и задаёте новый texts. НО в useCallback у вас в зависимостях тоже language, в результате после смены texts он уже не отрабатывает. Замените в useCallback зависимость на texts.
PS если это не в рамках обучения, то не советую писать свой инструмент для локализации сайта, лучше возьмите готовый, например i18n
PPS для подсказки в таких багах пусть и ненадёжно, но полезно использовать нейронные сети, к примеру chatgpt в целом хорошо расписывает если дать ему код вашего Хука и попросить найти баг, только что проверил даёт неплохие подсказки для начала. НО помните, что без опыта с нейронками легко выстрелить себе в ногу, тк не заметите где она там напридумывала всякую дичь.
при изменении language у меня не меняются переводы

Меняются. С запаздыванием на один шаг. Потому что texts обновляется в эффекте, т.е., после того, как новая версия t будет создана. Так что t получает ссылку на старую версию texts .

Никакого texts не надо, доставайте в t нужный объект из translations напрямую:

const t = useCallback(
  key => key
    .split('.')
    .reduce((p, c) => p?.[c], translations[language]),
  [ language ]
);
Похожие вопросы