'Using function defined in custom hook does not update some value in the state

i recently started using react and i'm trying to write a custom hook used for translating some ui elements. I'm not using a library because it is a larger project and it is expected to have a lot of custom loading/ translating parts.

Goal:

  • call the hook in every translatable component (in a very light syntax)
  • the hook will return a function used to translate elements in that component

So far, i tried this https://codesandbox.io/s/react-playground-forked-jt90ii?file=/MyComponent.js

Here is the custom hook:

import { useState, useEffect } from "react";

export default function useTranslation({ className, callbackFunction }) {
  const [translations, setTranslations] = useState([]);

  useEffect(() => {
    async function fetchTranslationsForClass(
      endpoint,
      setTranslations,
      className
    ) {
      // HERE WILL BE API CALL
      setTranslations([{ code: "HM", value: "Home" }]);

      if (callbackFunction) callbackFunction();
    }

    fetchTranslationsForClass("", setTranslations, className);
  }, []);

  return [
    (code) => {
      var a = internalT(translations, code);

      return a;
    },
    translations,
    setTranslations
  ];
}

function internalT(translations, translationCode, interpolationParam) {
  var tr = translations.filter((x) => x.code == translationCode);
  if (!interpolationParam) return tr && tr[0] && tr[0].value;
  else return tr && tr[0] && strInterpolate(tr[0].value);
}

const strInterpolate = (template, args = {}) => {
  const interpolateHandler = new Function(
    "params",
    "const __ = (" +
      Object.keys(args).join(",") +
      ") => `" +
      template +
      "`\nreturn __(...Object.values(params))"
  );
  return interpolateHandler(args);
};

That i'm trying to use like this:

import useTranslation from "./useTranslation";
import MyComponent2 from "./MyComponent2.js";

import { useState } from "react";

export default function MyComponent() {
  const [t, tr, setTr] = useTranslation("MyComponent", () => {});

  const [translatedItems, setTranslatedItems] = useState([{ name: t("HM") }]);

  return (
    <>
      <div>{t("HM")}</div>
      <MyComponent2 translatedItems={translatedItems} />
    </>
  );
}

And it works fine for string embedded in ui but it does not work for a string passed in component state in translatedItems;

In MyComponent2 item.name is always undefined:

export default function MyComponent2({ translatedItems }) {
  return (
    <>
      {translatedItems.map((item, index) => {
        return (
          <>
            <div>{"index" + index}</div>
            <div key={index}> {"'" + item.name + "'"}</div>
          </>
        );
      })}
    </>
  );
}

Basically the state is initialized at first render and it does not update after the 't' function is defined; If i try to set the state with useEffect this will create an infinite loop.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source