У меня есть следующий компонент React, который я хотел бы использовать в нескольких приложениях:
import React from "react";
import { useFactory } from "react-js-utl/hooks";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
/**
* Shows current account balance with variation since previous balance.
*/
const Balance = compose(React.memo)(function Balance({
current,
previous,
label,
sinceLabel,
increaseColor = "light-green",
increaseIcon = "arrow-up",
decreaseColor = "red",
decreaseIcon = "arrow-down",
sameColor = "blue",
sameIcon = "equals",
formatNum = void 0,
formatPerc = void 0
} = {}) {
// Compute the change.
let change = ((current - previous) / previous) * 100;
// Implementation detail. Switch objects depeding on runtime value of change.
const outcomeFactory = useFactory(
() => [
// Increase:
[
change > 0, // Condition, if true, the object following it will be returned by `useFactory()`.
{
outcome: "increase",
color: increaseColor,
icon: increaseIcon
}
],
// Decrease:
[
change < 0,
{
outcome: "decrease",
color: decreaseColor,
icon: decreaseIcon
}
],
// Same (default, returned if the previous conditions evaluate to false):
{
outcome: "same",
color: sameColor,
icon: sameIcon
}
],
// deps array
[
change,
increaseColor,
increaseIcon,
decreaseColor,
decreaseIcon,
sameColor,
sameIcon
]
);
change = Math.abs(change);
return (
<div className="balance">
<div className="balance-label">{label}</div>
<div className="balance-current">
{formatNum ? formatNum(current) : current}
</div>
<span
className={`balance-outcome balance-outcome-${outcomeFactory.outcome} balance-outcome-${outcomeFactory.color}`}
>
{typeof outcomeFactory.icon === "string" ? (
// Defaults to FontAwesome's icon.
<FontAwesomeIcon icon={outcomeFactory.icon} />
) : (
// Custom component rendered by client code.
outcomeFactory.icon
)}
{outcomeFactory.outcome === "same"
? // Same (no change since previous):
""
: // Increase or decrease since previous:
// Edge case: previous balance was zero
Number(previous) === 0
? ""
: // Percentage increase/decrease:
formatPerc
? formatPerc(change)
: change}
</span>
<span className="balance-since-label">{sinceLabel}</span>
</div>
);
});
Balance.displayName = "Balance";
export default Balance;
, который я использую в таком приложении:
...
function myFormattingFunctionForNumbers(t, num) {
// I use accounting.js to format numbers:
return accounting.format(num, {
decimal: t("formatting:numbers.decimal_separator"), // For EN lang this will be "."
thousand: t("formatting:numbers.thousand_separator") // For EN this this will be ","
})
}
function myFormattingFunctionForPercentages(t, perc) {
return myFormattingFunctionForNumbers(t, perc) + "%";
}
...
// Inside app component:
// Using react-i18next:
const { t, i18n } = useTranslation();
// If the language changes, so does formatting:
formatNum = useCallback(current => myFormattingFunctionForNumbers(t, current), [t, i18n.language])
formatPerc = useCallback(changePerc => myFormattingFunctionForPercentages(t, changePerc), [t, i18n.language])
...
<Balance
current={11234.56} // These hardcoded values could be props, of course.
previous={9321.45}
label={t("Your account balance")} // i18n
sinceLabel={t("Since previous month")} // i18n
formatNum={formatNum} // Function to format current value.
formatPerc={formatPerc} // Function to format change/variation percentage.
/>
...
, который выводит что-то выглядящее примерно так:
Your account balance
11,234.56
↑ 20,52% Since previous month
Сейчас я сталкиваюсь со следующими "проблемами":
Повторно используемый компонент является чистым (использует React.memo
), поэтому его функции форматирования formatNum
и formatPerc
должны меняться при изменении языка приложения, даже если другие реквизиты, такие как current
и previous
, не меняются, потому что другой язык потенциально предполагает другое форматирование и, следовательно, компонент должен перерисовываться;
Из-за пункта 1 клиент несет ответственность за соединение всех функций форматирования внутри потребляющего компонента, используя useCallback
, который создает много биологической таблицы ...;
Два useCallback
не предупреждают меня, что мне нужно передать i18n.language
в массив deps
, просто потому что на текущий язык не ссылаются напрямую из-за веселья при форматировании ctions myFormattingFunctionForNumbers
и myFormattingFunctionForPercentages
, которые используют только функцию t
i18next
(которая, насколько я знаю, не меняется при изменении языка);
Может быть есть точка 4 и даже точка 5, о которой я пока не знаю.
Какова текущая лучшая практика для повторно используемых компонентов React, которые поддерживают форматирование и i18n / l10n ?
Советы и рекомендации по организации кодирования форматирования / i18n / l10n и отделению этих проблем от повторно используемых компонентов будут приветствоваться.
Спасибо за внимание.