Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage of useTranslation hook inside components cause rerender #654

Closed
srgvrnv opened this issue Mar 18, 2020 · 33 comments
Closed

Usage of useTranslation hook inside components cause rerender #654

srgvrnv opened this issue Mar 18, 2020 · 33 comments

Comments

@srgvrnv
Copy link

srgvrnv commented Mar 18, 2020

Describe the bug

When I use useTranslation hook inside function component it causes multiple rerenders. Can't reproduce it with create-react-app and react-i18next. So it should be landed here. If I use react profiler I see that it takes 1102 render, but with create-react-app and react-i18next it takes only 2. Am I missing something or this is a bug?

Occurs in next-i18next version

4.2.1

Steps to reproduce

# i18n.js

const NextI18Next = require('next-i18next').default

module.exports = new NextI18Next({
    defaultLanguage: 'fi',
    otherLanguages: ['en', 'ru'],
    localeSubpaths: {
        en: 'en',
        ru: 'ru',
    },
})
# _app.js

import React from 'react';
import './App.css';
import Test from './components/test';
import i18n from './i18n';

const Locales = () => (
  <>
    {['fi', 'en', 'ru'].map(locale => (
      <button type="button" key={locale} onClick={() => { i18n.changeLanguage(locale); }}>
        {locale}
      </button>
    ))}
  </>
);

function App() {
  let tests = [];

  for (let index = 0; index < 100; index++) {
    let testsinner = [];
    for (let index2 = 0; index2 < 10; index2++) {
      testsinner.push(<Test key={`${index}_${index2}`} />);
    }
    tests.push(<Test key={index}>{testsinner}</Test>);
  }

  return (
    <>
      <Locales />
      <div>
        {tests}
      </div>
    </>
  );

}

export default App;
# components/test.jsx

import i18n from '../i18n';

let count = 0;

const Test = ({ children }) => {
    count++;

    const [t, { language }] = i18n.useTranslation();

    return (
        <>
            <p>{t('catalog')} + {count} + {language}</p>
            <div>{children}</div>
        </>
    )
}

export default Test;

Expected behaviour

Shouldn't be rendered so much times

OS (please complete the following information)

  • OS: macOS Catalina
  • Browser: Chrome 80.0.3987.132
@isaachinman
Copy link
Contributor

Hi @srgvrnv - thanks for this report. Do you mind quickly setting up a demo repo that we can reproduce this and track down the issue?

@srgvrnv
Copy link
Author

srgvrnv commented Mar 18, 2020

Hi!
Sure, here it is

@srgvrnv
Copy link
Author

srgvrnv commented Mar 23, 2020

@isaachinman Hello! Any updates on this issue?

@isaachinman
Copy link
Contributor

@srgvrnv I don't really know what you expect, you're rendering 1100 components in a loop.

@srgvrnv
Copy link
Author

srgvrnv commented Apr 6, 2020

@isaachinman

Size of loop is ridiculously huge just to demonstrate a problem. I face this issue while rendering nested set of 50 entities with nesting level of 2 using of useTranslation() hook inside each of them.

Anyway react-i18next have no issue to render "1100 components in a loop" while next-i18next have some problems with it

Maybe there is some misunderstanding but I expect it to work same way

Am I missing something here?

@ricardobrandao
Copy link

I'm having the same issue. @srgvrnv were you able to understand what the issue was?

Best,

@srgvrnv
Copy link
Author

srgvrnv commented May 11, 2020

@ricardobrandao Unfortunately not. For now I solve it with using static class which I setup in _app.tsx instead of using useTranslation()

LocaleHandler.locale = language;
LocaleHandler.t = t;

@chrisinajar
Copy link

The example repository posted above clearly demonstrates that next-i18n results in 1 extra render per language change than react-i18n does. Each full re-render using the react variant causes 1,100 renders -- one each. The next variant causes each component to render twice, 2,200 renders total.

This seems to be caused by the entire App tree being re-rendered instead of just the components that change. You'll notice that in the example above, the core app component in the CRA version never renders again after the first render, while in the next version the App renders every single time and then re-renders all of the children once more as well (needlessly double rendering all of them).

This can be very easily observed by simply adding log statements to the render methods:
image
Here you can see all of the components render with the new language after the change, and then the app itself re-renders causing them all to render once more. My guess is something to do with putting the language in the url...

Anyway, I hope this additional information helps. This ticket should be re-opened as there is clearly either an undocumented inconsistency or an actual bug.

@isaachinman isaachinman reopened this May 12, 2020
@isaachinman
Copy link
Contributor

Don't have a lot of spare time at the moment, but very happy to hear any suggestions/ideas.

@SalahAdDin
Copy link

Is it possible to use react-i18n instead of this package?

@isaachinman
Copy link
Contributor

@SalahAdDin Of course.

@SalahAdDin

This comment has been minimized.

@ddhp
Copy link

ddhp commented Jul 27, 2020

I found out whenever the language is changed, the router would be replaced which is causing the extra rerendering (source code). Probably this is the expected behavior, since next.js also handles the routing part but create-react-app doesn't?

@srgvrnv you said you fixed this issue by setting static values, but are you still using appWithTranslation to wrap your App? Also thanks for your repo which is very helpful to debug

@k4mr4n
Copy link

k4mr4n commented Nov 6, 2020

I have the same issue and whydidyourender library point it out very well.
it seems like the "t" function exported from "useTranslation" hook has not been memoized.

Screen Shot 2020-11-06 at 9 28 05 AM

@tquiroga
Copy link

Same issue pointed by whydidyourender, when you use the hook across your app and realize most re-render are caused by the useTranslation hook, ouch 😬

@isaachinman
Copy link
Contributor

Does anyone mind testing this issue in next-i18next@8.0.0 to see if it is still relevant?

@zcmgyu
Copy link

zcmgyu commented Apr 5, 2021

I don't know why but I got the same issue with react-i18next. LOL

@k4mr4n
Copy link

k4mr4n commented Apr 5, 2021

@isaachinman upgrading to v8.0.0 from v6 is really painful. would there be any patch for the v6 to fix this issue?

@isaachinman
Copy link
Contributor

@k4mr4n As with all previous releases, I won't be actively working on maintenance, but if users want to open PRs, I will certainly review, merge, and publish them.

@asmeeee
Copy link

asmeeee commented Jul 7, 2021

For anyone interested, re-render is still happening to me (probably should at some point), but I ended up using next-translate and it seems to be working fine.

@SalahAdDin
Copy link

@andrei-scripcaru why did you switched to next-translate?

@asmeeee
Copy link

asmeeee commented Jul 7, 2021

@andrei-scripcaru why did you switched to next-translate?

Switching the language was causing children (not sure exactly which ones) to re-render and there's no re-rendering with next-translate. I believe it has to do something with getStaticProps since if commented it works as expected.

@SalahAdDin
Copy link

@andrei-scripcaru why did you switched to next-translate?

Switching the language was causing children (not sure exactly which ones) to re-render and there's no re-rendering with next-translate. I believe it has to do something with getStaticProps since if commented it works as expected.

@isaachinman

@isaachinman
Copy link
Contributor

@SalahAdDin What was the intention of the @?

If anyone can provide reproducible examples of faulty behaviour, issues will be fixed quickly.

@muhammadabdulazim
Copy link

any solution ?

@houssemmimoun27
Copy link

Create a file Translate.js
import { useTranslation } from "react-i18next";

function Translate(props) {
const { t } = useTranslation();
return t(props.value);
}
export default Translate;

then add this function in the component

Thats solution fix the problem of rerendering the component.

@Poyoman39
Copy link

Poyoman39 commented Apr 20, 2022

There is this effect in source code causing useTranslation to refresh the "t" function
(i'm using suspense)

  var isInitial = useRef(true);

  useEffect(function () {
    if (isMounted.current && !isInitial.current) {
      setT(getT);
    }

    isInitial.current = false;
  }, [i18n]);

@Poyoman39
Copy link

Poyoman39 commented Apr 20, 2022

Guys ... could it be your bug ??? (this was my one)
https://stackoverflow.com/questions/61254372/my-react-component-is-rendering-twice-because-of-strict-mode

Try disabling React.StrictMode !!!! ;)

@isaachinman
Copy link
Contributor

@Poyoman39 That is not next-i18next code.

@npearson72
Copy link

I know this is a pretty old thread, but I've found that useTranslation causes a double render when the component is used within React Router.

@viktorkasap
Copy link

з "react-i18ne

Only i18n - and we have 2 renders. Why?

https://codesandbox.io/s/i18next-10-rerender-not-fixed-4eydfm?file=/src/App.js

@allangrds
Copy link

The problem persists:

https://codesandbox.io/s/heuristic-agnesi-9t91nh

Captura de Tela 2023-01-17 às 11 53 00

I now React's StrictMode will cause a double render, but if we divide the Children's render by 2, we'll still have a render.

@adrai
Copy link
Member

adrai commented Jan 17, 2023

without strict mode i see only 1 render:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests