React Hook "useTranslation" is called in function "getMyMenu" that is neither a React function component nor a custom React Hook function

Viewed 2318

I am invoking the useTranslation hook from within a normal function as below

import { useTranslation } from "react-i18next";


function getMyMenu(a) {
  const { t } = useTranslation('translations');
  
  if (a.showRoute) {
      a.label = t(a.label);
    return a;
  }
}



export const MyNav = props => {
  let tabs = recurseFn(getMyMenu, props.routes);
}

I get the following error. Is there no way to fix this? For some strange reason, I was able to see the similar code pattern working in other project. Any additional config needed to make this work?

Updated:

This is how my recurseFn looks like:

export const recurseFn = (chk, [head, ...tail]) => {
  if (head === undefined) return [];
  if (chk(head)) {
    return [head, ...recurseFn(chk, tail)];
  }

  return [...recurseFn(chk, tail)];
};
2 Answers

You can fix it by moving the useTranslation hook to the MyNav component body and pass the translation function t as a closure in getMyMenu.

import { useTranslation } from "react-i18next";

export const MyNav = props => {
  const { t } = useTranslation('translations');

  function getMyMenu(a) {
    if (a.showRoute) {
      a.label = t(a.label);
      return a;
    }
  }

  let tabs = recurseFn(getMyMenu, props.routes);

  ...
}

Edit

I just updated with how my recurseFn looks like. Is it possible to pass "t" to it?

Solution 1 - Pass t explicitly to all functions

You could certainly update the function signature of recurseFn to also consume a "t" (translation?) function, but you'd still need to then also pass t into chk which would necessitate updating the original getMyMenu function to consume an additional argument.

Example:

function getMyMenu(t, a) { // <-- consume additional t argument
  if (a.showRoute) {
      a.label = t(a.label);
    return a;
  }
}

...

export const recurseFn = (chk, [head, ...tail], t) => { // <-- consume additional t argument
  if (head === undefined) return [];
  if (chk(t, head)) { // <-- pass t to chk
    return [head, ...recurseFn(chk, tail, t)]; // <-- pass t on recursion
  }

  return [...recurseFn(chk, tail, t)]; // <-- pass t on recursion
};

...

import { useTranslation } from "react-i18next";

export const MyNav = props => {
  const { t } = useTranslation('translations');

  let tabs = recurseFn(getMyMenu, props.routes, t); // <-- pass t on recursion

  ...
}

Solution 2 - A better solution using a curried callback

In this case I think making getMyMenu a curried function can help you enclose t in the callback and allow it to be declared externally to any React component. The recurseFn function doesn't need to know anything about t, so why expose it there, right?

const getMyMenu = t => a => {
  if (a.showRoute) {
    a.label = t(a.label);
    return a;
  }
}

Now you destructure t from the useTranslation hook return value and pass to the curried getMyMenu function. This is a similar idea to closing over the t function in the callback like we did above in the first answer.

import { useTranslation } from "react-i18next";

export const MyNav = props => {
  const { t } = useTranslation('translations');

  let tabs = recurseFn(getMyMenu(t), props.routes);

  ...
}

Now from recurseFn's perspective, t is enclosed in the chk callback and recurseFn didn't need to explicitly handle receiving and passing t around.

I was able to solve this same issue by ensuring that the function name was capitalized. The complaint explicitly stated that "React component names must start with a an uppercase letter." After correcting this issue the ESLint message vanished.

Related