welldone-software / why-did-you-render

why-did-you-render by Welldone Software monkey patches React to notify you about potentially avoidable re-renders. (Works with React Native as well.)
https://www.npmjs.com/package/@welldone-software/why-did-you-render
MIT License
11.21k stars 201 forks source link

Next.js integration #84

Closed mikestopcontinues closed 4 years ago

mikestopcontinues commented 4 years ago

I've tried a number of locations, and I can't seem to make this work with Next. What's the recommended solution? Thanks!

vzaidman commented 4 years ago

I got to add it to the next.js examples indeed.

you can add it as an entry in next.config.js:

 webpack: (config, {dev, isServer, defaultLoaders}) => {
.....
    if (dev && !isServer) {
      const originalEntry = config.entry;
      config.entry = async () => {
        const entries = await originalEntry();
        if (entries['main.js'] && !entries['main.js'].includes('./common/whyDidYouRender.js')) {
          entries['main.js'].unshift('./common/whyDidYouRender.js');
        }
        return entries;
      };
    }

where ./common/whyDidYouRender.js looks like this:

import React from 'react';
import whyDidYouRender from '@welldone-software/why-did-you-render';

whyDidYouRender(React);
vzaidman commented 4 years ago

If anybody wants to open a next.js example here: https://github.com/zeit/next.js/tree/master/examples

please notify me in this ticket.

motiko commented 4 years ago

I've created a PR https://github.com/zeit/next.js/pull/10662

vzaidman commented 4 years ago

I've created a PR zeit/next.js#10662

beautiful! when it's merged, you can open a PR with a link to the example added to the readme, so you would become a contributer of this library as well! Thank you!

motiko commented 4 years ago

Quick update, message from maintainers:

This example is modifying the main entry using custom webpack configuration, let's try to avoid these kind of changes as they may cause unexpected issues, like hydration errors.

Please use a different method or give more information about why does it have to be this way 🙏

You can see my answer here . Hope it is enough and feel free to jump into the conversation if you feel like it @vzaidman 😃

vzaidman commented 4 years ago

here is a working example of integration with next.js. If for some reason the proposed way doesn't work, you can always try using the more intrusive but reliable way I proposed at the comment above.

mikestopcontinues commented 4 years ago

With one of the recent Next updates, neither one of these methods work.

@vzaidman config.entry is now a function. Wrapping and resolving the function, then prepending the whyDidYouRender script completely breaks Next.

And @motiko, I'm sorry to say your solution simply doesn't result in any output.

Do either of you have any other ideas?

francescovenica commented 4 years ago

are these solutions working on the last versions of wdyr and next? I can't get it working...no errore but nothing in console

mikestopcontinues commented 4 years ago

@francescovenica I still haven't figured it out, and I gave it a good go.

Vadorequest commented 4 years ago

I'll give it a try. I just stumbled upon WDYU and I think that'd be a great addition to https://github.com/UnlyEd/next-right-now

Maybe I'll add into https://github.com/UnlyEd/next-right-now/pull/42 if I get it to work.

Vadorequest commented 4 years ago

I've tried to add the following in _app, and while it doesn't seem to create any issue, it doesn't seem to do anything either. I've never used WDYU before so I'm not quite sure what to expect to be printed on the console. Could someone else confirm it does/not work?

if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  // eslint-disable-next-line no-console
  console.debug('Applying whyDidYouRender');
  whyDidYouRender(React);
}
vzaidman commented 4 years ago

try

if (process.env.NODE_ENV === 'development') {
  if (typeof window !== 'undefined') {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const whyDidYouRender = require('@welldone-software/why-did-you-render');
    whyDidYouRender(React, {
      trackAllPureComponents: true
    });
  }
}
Vadorequest commented 4 years ago

Much better, thanks!

Getting this now:

image

So, it seems there are a few useless re-renders (2, at least), but I don't see how those information can help me knowing from what components they came from.

I tried, but can't locate the component that re-renders unnecessarily. Is that the expected behaviour?

Vadorequest commented 4 years ago

Alright, I also get this and understand a bit more how the logging is meant to work.

image

Basically, it seems to be working fine to me, it's just that the ReactAsyncHighlighter wasn't a component I had expected (it's not one of mine, so the name didn't trigger any bell, unlike Code and CodeBlock)

So, looks like https://github.com/welldone-software/why-did-you-render/issues/84#issuecomment-634098621 does the job for Next.js. :)

vzaidman commented 4 years ago

sadly it seems like issue https://github.com/welldone-software/why-did-you-render/issues/114 that i didn't solve yet.

look inside for a workaround (in short it is assigning ReactAsyncHighlighter.whyDidYouRender = false)

Vadorequest commented 4 years ago

Thanks for the link, I understand the underlying issue, it's unrelated to Next.js.

@francescovenica @mikestopcontinues https://github.com/welldone-software/why-did-you-render/issues/84#issuecomment-634098621 should do the trick for your Next.js app. I have it working using Next 9.4.0.

My commit: https://github.com/UnlyEd/next-right-now/pull/52/commits/b54f416c6626a8de7bba0087e6203d55286be9d8

nandorojo commented 3 years ago

Just checking in here, does this work if I do it in a Next.js app?

if (process.env.NODE_ENV === 'development') {
  if (typeof window !== 'undefined') {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const whyDidYouRender = require('@welldone-software/why-did-you-render');
    whyDidYouRender(React, {
      trackAllPureComponents: true
    });
  }
}
Vadorequest commented 3 years ago

@nandorojo Here's what I did: https://github.com/UnlyEd/next-right-now/blob/0174dcc21e36dd356e2be475bfca5339baf27fd3/src/modules/core/wdyr/wdyr.tsx

Imported from _app: https://github.com/UnlyEd/next-right-now/blob/0174dcc21e36dd356e2be475bfca5339baf27fd3/src/pages/_app.tsx#L10

And with a custom CSS style too: https://github.com/UnlyEd/next-right-now/blob/0174dcc21e36dd356e2be475bfca5339baf27fd3/src/layouts/core/components/Head.tsx#L110-L117

ivadenis commented 2 years ago

Tried different approaches but wasnt able to make it work with next 12. No console output.

dwilt commented 2 years ago

@ivadenis did you try wiping the .next build folder and then re-running the dev server (next dev)?

rm -rf .next; next dev

For me, in our next project, I've noticed that WDYR doesn't enable unless you wipe the build folder

ktravelet commented 2 years ago

hey all, having issues here too. At the top of of wdyr.tsx I put the following logger, and typeof window is always undefined.

console.log(
  `testing if WDYR.  (typeof window): ${
    typeof window
  }. process.env.NODE_ENV: ${process.env.NODE_ENV}`
);
hems commented 2 years ago

Tried different approaches but wasnt able to make it work with next 12. No console output.

any updates on this?

apperside commented 1 year ago

There was no way to make it work for me: I added webpack config as suggested by @vzaidman , I ensured that it was loading wdyr.ts file by adding a log, I tried serveral babel tricks, but nothing worked untill I added the following

"jsxImportSource": "@welldone-software/why-did-you-render"

to tsconfig.ts

From that moment onward, it started finally working!

hems commented 1 year ago

Can anybody confirm the example from the README works on Next 12?

Anybody tried it with Next 13 yet?

rocketana commented 1 year ago

I tried all solutions from this thread, none of them works with Next,js 13.

Importing whyDidYouRender.js script in the root layout did nothing (well, it shouldn't, as it's a server component).

Adding it to the next.config webpack did nothing. I tested that it's added by printing webpack entries to the console, the absolute file path was there. Logging from the file never happened.

Importing it in a top level client component did also nothing. And logging from the script was printed to both, the browser console and the server terminal, it looks like it runs on the server being imported in a client component? (Sounds like I don't understand something, I'm still learning the concepts of Next.js 13 and client/server components). Anyway, it didn't work this way.

I then tried to init whyDidYouRender directly in a top level client component. It then starts to break the app with an error React has detected a change in the order of Hooks caused by HotReload and Router.

   Previous render            Next render
   ------------------------------------------------------
1. useMemo                    useMemo
2. useRef                     useRef
3. useRef                     useRef
4. useEffect                  useEffect
5. useReducer                 useReducer
6. useCallback                useRef
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The problem with hooks order's disappeared sometimes for an unknown reason. A few times I was able to see the reasons for re-render after navigating successfully. But 99/100 times I just get the error about hooks order. With the same code and same actions (I just open a page and try to navigate using next.js links).

It works fine until you try to navigate. I tested adding the following code to the top level client component and a child component. I got the reason as expected for both of them. I passed no options on the init.

    const [objState, setObjState] = useState({ name: 'World' })

    useEffect(() => {
        setObjState({ name: 'World' })
    }, [])
hems commented 1 year ago

I tried all solutions from this thread, none of them works with Next,js 13.

Unfortunately i didn't have time to test it with the new app folder yet, but i created a new issue ( since this one is closed ) asking for help with the new app folder.

You can see the new issue here

I also pointed to an example from the official documentation that hopefully will be helpful in your situation.

AlbinoGeek commented 1 year ago

I can't get this working in NextJS 13 either, even in pages/ as normal.

Also tried everything in this thread, including the jsxImportSource.

Simply put, the import does literally nothing, no changes to output.


src/pages/_app.tsx

import 'wdyr'

// ! Keep this newline, otherwise 'wdyr' gets organized lower in the imports.

import { CacheProvider } from '@emotion/react'
import type { EmotionCache } from '@emotion/utils'
import Box from '@mui/material/Box'
import CssBaseline from '@mui/material/CssBaseline'
import type { Theme } from '@mui/material/styles'

// ...

export default MyApp

src/wdyr.ts

/**
 * WDYR (why-did-you-render) helps locate unnecessary re-renders.
 * Applied in development environment, on the frontend only.
 *
 * It will only log unnecessary re-renders, not expected re-renders.
 *
 * @see https://github.com/welldone-software/why-did-you-render
 * @see https://github.com/vercel/next.js/tree/canary/examples/with-why-did-you-render
 */
import React from 'react'

if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const whyDidYouRender = require('@welldone-software/why-did-you-render')

  // eslint-disable-next-line no-console
  console.debug('Applying whyDidYouRender, to help you locate unnecessary re-renders during development. See https://github.com/welldone-software/why-did-you-render')

  // See https://github.com/welldone-software/why-did-you-render#options
  whyDidYouRender(React, {
    trackAllPureComponents: true,
    trackHooks:             true,
    logOwnerReasons:        true,
    collapseGroups:         true,
  })
}

package.json

{
  "name": "none-may-know",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev -p 9000",
    "build": "next build",
    "export": "next export",
    "start": "next start -p 9000",
    "lint": "next lint"
  },
  "dependencies": {
    "@emotion/cache": "^11.11.0",
    "@emotion/react": "^11.11.1",
    "@emotion/server": "^11.11.0",
    "@emotion/styled": "^11.11.0",
    "@emotion/utils": "^1.2.1",
    "@mui/icons-material": "^5.14.1",
    "@mui/lab": "^5.0.0-alpha.139",
    "@mui/material": "^5.14.1",
    "@mui/system": "^5.14.1",
    "next": "^13.4.12",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-use": "^17.4.0",
    "sharp": "^0.32.4",
    "swr": "^2.2.0"
  },
  "devDependencies": {
    "@next/eslint-plugin-next": "^13.4.12",
    "@types/node": "^20.4.4",
    "@types/react": "^18.2.16",
    "@types/react-dom": "^18.2.7",
    "@typescript-eslint/eslint-plugin": "^6.2.0",
    "@typescript-eslint/parser": "^6.2.0",
    "@welldone-software/why-did-you-render": "^7.0.1",
    "eslint": "^8.45.0",
    "eslint-config-next": "^13.4.12",
    "eslint-plugin-react": "^7.33.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "start-server-and-test": "^2.0.0",
    "typescript": "^5.1.6"
  },
  "packageManager": "yarn@3.2.0"
}

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "src",
    "target": "ESNext",
    "lib": ["dom", "dom.iterable", "ESNext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "jsxImportSource": "@welldone-software/why-did-you-render",
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}
YoannBuzenet commented 3 months ago

Can't make it work neither. Did anyone find something?