react i18next introduced a change in the order of Hooks using i18n

Viewed 306

I have a reusable component used many times to display different dropdown buttons. It uses i18n.

Here is my code:

import React from 'react';
import { Dropdown } from 'react-bootstrap';
import shortid from 'shortid';
import { useTranslation } from 'react-i18next';

const PropertyButton = (state, name, handle, list, i18nName) => {

    if (name === "Produttore") {
        console.log("aa")
     }

     const [ t, i18n ] = useTranslation();
    
        return (
               <Dropdown>
                    <Dropdown.Toggle className="choiceButton servingButton btn" key="{shortid.generate()}" 
                        item={name} onSelect={handle}>
                        {t(`wine.attribute.${name}`)}
                    </Dropdown.Toggle>

                    <Dropdown.Menu className="choiceButton servingButton btn">

                        { list === null || list === undefined ? <></> :
                        list.map(item => {
                            const val = item.name === undefined ? item : item.name;
                            return (
                                <Dropdown.Item key={shortid.generate()} kname={name} kvalue={val} onClick={handle}>
                                   { i18nName === "regionList" || i18nName === "produttore" || i18nName === "venditore" ? val : t(`wine.${i18nName}.${val}`)}
                                </Dropdown.Item>
                            )
                        }
                        )}
                    </Dropdown.Menu>
                </Dropdown>
        )
    };

export default PropertyButton;

The component is called several times, with no error. I don't think the problem is in the body of the component, because it only shows a button. There should be something weird in i18n or in the way I used it.

There is a case, when name === "Produttore" (and I added a console.log just to put a breakpoint), then stepping on the const [ t, i18n ] = useTranslation(); line generates an hook error.

BTW, I used both const [ t, i18n ] = useTranslation(); and const { t } = useTranslation(); options with the identical outcome.

Warning: React has detected a change in the order of Hooks called by NickSelection. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

************* SHOWING HOW IT IS USED **********************

As requested, here is how the Component is used/called. Each call to a PropertyButton returns a Dropdown button, initialized with the passed parameters.

All of them are running (in different languages) but only the one with Produttore (signaled with the ===>>> arrow) generate the error.

const NickSelection = ({
    regionList,
    mainListContainsEffervescenza, mainListContainsTannicità,
    prezzo, setPrezzo,
    effervescenza, setEffervescenza,
    acidità, setAcidità,
    alcolicità, setAlcolicità,
    tannicità, setTannicità,
    tipologia, setTipologia,
    territorio, setTerritorio,
    produttore, setProduttore, producerList,
    venditore, setVenditore, sellerList
}) => {

    return (

            {/* <Footer */}
            <div className="footer_container footer_container_chat" style={{ zIndex: "6000" }}>
      {PropertyButton(tipologia, "Tipologia", handlePropertyButton, allowedProductTypes, "productType")}
      {PropertyButton(territorio, "Territorio", handlePropertyButton, regionList, "regionList")}
      {PropertyButton(acidità, "Acidità", handlePropertyButton, wineSensations, "sensation")}


      {mainListContainsEffervescenza ? PropertyButton(effervescenza, "Effervescenza", handlePropertyButton, wineSensationsWith0, "sensation") : <></>}
      {PropertyButton(alcolicità, "Alcolicità", handlePropertyButton, wineSensations, "sensation")}
      {mainListContainsTannicità ? PropertyButton(tannicità, "Tannicità", handlePropertyButton, wineSensationsWith0, "sensation") : <></>}
      {PropertyButton(prezzo, "Prezzo", handlePropertyButton, prices, "prices")}

      { producerList.length > 1 ? 
===>>>  PropertyButton(produttore, "Produttore", handlePropertyButton, producerList, "produttore") : <></> }

      { sellerList.length > 1 ? PropertyButton(venditore, "Venditore", handlePropertyButton, sellerList, "venditore") : <></>}

   </div>

};

export default NickSelection;

1 Answers

Issue

You've declared a regular Javascript function, PropertyButton and invoke it directly in your code. Eventually you are conditionally calling this function, and thus, the order of hooks called has changed.

<div className="...." style={{ ... }}>
  {PropertyButton(tipologia, "Tipologia", handlePropertyButton, allowedProductTypes, "productType")}
  {PropertyButton(territorio, "Territorio", handlePropertyButton, regionList, "regionList")}
  {PropertyButton(acidità, "Acidità", handlePropertyButton, wineSensations, "sensation")}

  {mainListContainsEffervescenza ? PropertyButton(effervescenza, "Effervescenza", handlePropertyButton, wineSensationsWith0, "sensation") : <></>}
  {PropertyButton(alcolicità, "Alcolicità", handlePropertyButton, wineSensations, "sensation")}
  {mainListContainsTannicità ? PropertyButton(tannicità, "Tannicità", handlePropertyButton, wineSensationsWith0, "sensation") : <></>}
  {PropertyButton(prezzo, "Prezzo", handlePropertyButton, prices, "prices")}

  {producerList.length > 1 ? PropertyButton(produttore, "Produttore", handlePropertyButton, producerList, "produttore") : <></> }

  {sellerList.length > 1 ? PropertyButton(venditore, "Venditore", handlePropertyButton, sellerList, "venditore") : <></>}

This is a violation of the Rules of Hooks.

Solution

Try converting PropertyButton into a React component, rendered as JSX.

import React from 'react';
import { Dropdown } from 'react-bootstrap';
import shortid from 'shortid';
import { useTranslation } from 'react-i18next';

const PropertyButton = ({ state, name, handle, list, i18nName }) => {
  ...
};

export default PropertyButton;

...

<div className="...." style={{ ... }}>
  <PropertyButton
    state={tipologia}
    name="Tipologia"
    handle={handlePropertyButton}
    list={allowedProductTypes}
    i18nName="productType"
  />
  ...etc...

  {mainListContainsEffervescenza && (
    <PropertyButton
      state={effervescenza}
      name="Effervescenza"
      handle={handlePropertyButton}
      list={wineSensationsWith0}
      i18nName="sensation"
    />
  )}
  ...etc...

  {producerList.length > 1 && (
    <PropertyButton
      state={produttore}
      name="Produttore"
      handle={handlePropertyButton}
      list={producerList}
      i18nName="produttore"
    />
  )}

  ...etc...

</div>
Related