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

Support create-react-app/react-scripts 4 #154

Open
ggascoigne opened this issue Oct 31, 2020 · 65 comments
Open

Support create-react-app/react-scripts 4 #154

ggascoigne opened this issue Oct 31, 2020 · 65 comments
Labels
workaround available A workaround is available in the issue's comments

Comments

@ggascoigne
Copy link

I'm not sure what part of the system causes things to break, but using create-react-app 4 seems to effectively disable why-did-you-render.

Switching back to react-scripts 3.4.4 fixes things.

@vzaidman
Copy link
Collaborator

React 17 support is incoming in version 6 which is in progress.
#155

@ggascoigne
Copy link
Author

I saw that, but cra 4 also breaks with react 16.

@FezVrasta
Copy link

Probably you are using the new JSX transform, I don't think it's supported right now.

@vzaidman
Copy link
Collaborator

vzaidman commented Nov 2, 2020

" cra 4 also breaks with react 16."
react 16 is not supported with the new JSX transform right?

@FezVrasta
Copy link

It is, they released a new version to support it.

@vzaidman
Copy link
Collaborator

vzaidman commented Nov 2, 2020

oh right right. i hope ill get to fixing it asap

@vzaidman
Copy link
Collaborator

vzaidman commented Nov 13, 2020

Can you please try version v6.0.0-rc.1 ?
@ggascoigne @FezVrasta
I'm about to release it.
notice in the readme how it has to be installed to support the new jsx transformation:
https://github.com/welldone-software/why-did-you-render#setup

EDIT: This is a good idea, but can't be implemented in CRA4.

@FezVrasta
Copy link

How can I import it before react hot loader in Create React App?

@vzaidman
Copy link
Collaborator

It seems to me like the library will not currently support CRA.
even react-app-rewired can't help...

@vzaidman
Copy link
Collaborator

sadly, CRA is not flexible enough to support the patching of the new JSX transformation.
this means you can either eject or run without it by changing the start script in package.json:

    "start": "cross-env DISABLE_NEW_JSX_TRANSFORM=true react-scripts start",

@vzaidman vzaidman added bug Something isn't working workaround available A workaround is available in the issue's comments labels Nov 13, 2020
@FezVrasta
Copy link

Any chance you could use Babel macros to overcome the limitation?

@mrlubos
Copy link

mrlubos commented Nov 23, 2020

Hi @vzaidman, wondering what's the issue with CRA everyone is mentioning here? The new React update should be concerned only with the JSX factory, no? What changed in CRA?

@vzaidman
Copy link
Collaborator

vzaidman commented Nov 23, 2020

it's seems to be kind of impossible to reconfigure it's webpack and babel.
we used to use tools like react-app-rewired, but they don't work anymore:
timarney/react-app-rewired#500

And it's needed in order to use WDYR with the new JSX.
Also with react-redux: #85

@mrlubos
Copy link

mrlubos commented Nov 23, 2020

@vzaidman Are you able to use https://github.com/gsoft-inc/craco to achieve what you're trying to do?

@vzaidman
Copy link
Collaborator

It seems like it doesn't support CRA4 as well:
dilanx/craco#205

@FezVrasta
Copy link

Any chance you could use Babel macros to overcome the limitation?

Bump? I can't believe Babel can't help here.

@vzaidman
Copy link
Collaborator

vzaidman commented Nov 24, 2020

what i need is to reconfigure the JSX transformation plugin (in @babel/preset-react):

['@babel/preset-react', {
  runtime: 'automatic',
  development: process.env.NODE_ENV === 'development',
  importSource: '@welldone-software/why-did-you-render',
}]

do you think it's possible with CRA 4?

@FezVrasta
Copy link

FezVrasta commented Nov 24, 2020

Would it be enough to alias react with @welldone-software/why-did-you-render?

@vzaidman
Copy link
Collaborator

It might work.

@FezVrasta
Copy link

In that case, maybe it's not very ergonomic, but one could use the resolutions field of Yarn to replace react with @welldone-software/why-did-you-render. Does the package export everything the original react package exports?

@vzaidman
Copy link
Collaborator

vzaidman commented Nov 24, 2020

ok i got it to work with craco.

Step 1:
understand and install craco

Step 2:
use the following config to override @babel/preset-react:

const presetReact = require('@babel/preset-react').default;
const presetCRA = require('babel-preset-react-app');

module.exports = {
  babel: {
    loaderOptions: (babelLoaderOptions, { env, paths }) => {
      const origBabelPresetReactAppIndex = babelLoaderOptions.presets.findIndex(preset => {
        return preset[0].includes('babel-preset-react-app')
      })

      if (origBabelPresetReactAppIndex === -1) {
        return babelLoaderOptions;
      }

      const overridenBabelPresetReactApp = (...args) => {
        const babelPresetReactAppResult = presetCRA(...args);
        const origPresetReact = babelPresetReactAppResult.presets.find(preset => {
          return preset[0] === presetReact;
        });
        Object.assign(origPresetReact[1], {
          importSource: '@welldone-software/why-did-you-render',
        });
        return babelPresetReactAppResult;
      }

      babelLoaderOptions.presets[origBabelPresetReactAppIndex] = overridenBabelPresetReactApp;

      return babelLoaderOptions;
    }
  },
};

@vzaidman
Copy link
Collaborator

In that case, maybe it's not very ergonomic, but one could use the resolutions field of Yarn to replace react with @welldone-software/why-did-you-render. Does the package export everything the original react package exports?

It sounds too intrusive to me.

@vzaidman vzaidman removed the bug Something isn't working label Nov 24, 2020
@FezVrasta
Copy link

FezVrasta commented Nov 24, 2020

I just realized the @babel/preset-react plugin should support a /** @jsxImportSource react */ comment.

If we use that in index.js and set it to /** @jsxImportSource @welldone-software/why-did-you-render */ would the lib work?

edit:

can confirm it works!!! The following is enough to make CRA work:

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

if (process.env.NODE_ENV === 'development') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React, {
    trackAllPureComponents: true,
  });
}

@vzaidman
Copy link
Collaborator

vzaidman commented Nov 24, 2020

Wait, does it changes the jsxImportSource for all files?

@FezVrasta
Copy link

I suppose it only does that for the one in the wdyr.js module, but from what I could observe everything is working good all around the app

@vzaidman
Copy link
Collaborator

it seems like it needs to be added to all files.
maybe it's possible to do with babel macros?

@FezVrasta
Copy link

The code I provided is enough, why you say it's not? I tested it in my app and all the components were properly instrumented

@0xdevalias
Copy link

0xdevalias commented Jan 5, 2021

@nicolas-rohricht When I have had that in the past (unrelated to this project) it’s due to my component’s displayName not being known. In the main case that I saw it, it was when I was using a => anonymous function within React.memo, causing React to not be able to get the displayName. Solutions were to explicitly set the displayName, or to use a named function instead.

If you Google something like “why is my react component showing as unknown in dev tools” or something like that I believe there should be a whole pile of useful resources on the topic.

@vzaidman
Copy link
Collaborator

vzaidman commented Jan 5, 2021

The only thing that is giving me a pain in the neck is that i'm getting Unknown here

see the solutions proposed there:
#163

as a rule of law, better wrap components with memo after creating them:

const Comp = props => ...

export default React.memo(Comp)

but there are some more options in the bug linked above

@lutaiphong
Copy link

@bmoore235 I followed your post and it worked perfectly 👍🏼 . Thanks for the update.

@vzaidman
Copy link
Collaborator

vzaidman commented Feb 5, 2021

  1. install craco: https://github.com/gsoft-inc/craco
  2. craco.config.js:
module.exports = {
    babel: {
        loaderOptions: (babelLoaderOptions) => {
            const origBabelPresetCRAIndex = babelLoaderOptions.presets.findIndex((preset) => {
                return preset[0].includes('babel-preset-react-app');
            });

            const origBabelPresetCRA = babelLoaderOptions.presets[origBabelPresetCRAIndex];

            babelLoaderOptions.presets[origBabelPresetCRAIndex] = function overridenPresetCRA(api, opts, env) {
                const babelPresetCRAResult = require(
                    origBabelPresetCRA[0]
                )(api, origBabelPresetCRA[1], env);

                babelPresetCRAResult.presets.forEach(preset => {
                    // detect @babel/preset-react with {development: true, runtime: 'automatic'}
                    const isReactPreset = (
                      preset && preset[1] &&
                      preset[1].runtime === 'automatic' &&
                      preset[1].development === true
                    );
                    if (isReactPreset) {
                        preset[1].importSource = '@welldone-software/why-did-you-render';
                    }
                })

                return babelPresetCRAResult;
            };

            return babelLoaderOptions;
        },
    },
    // if you want to track react-redux selectors
    webpack: {
      alias: {
        'react-redux': process.env.NODE_ENV === 'development' ? 'react-redux/lib' : 'react-redux'
      }
    }
};

@Niks218
Copy link

Niks218 commented Feb 9, 2021

I managed to make it working with react-app-rewired by adding this to the end of override config

(config) => {
    const options = getBabelLoader(config).options;

    const originalPreset = options.presets.find((preset) => preset[0].includes('babel-preset-react-app'));
    if (originalPreset) {
       Object.assign(originalPreset[1], {
         development: true,
         importSource: '@welldone-software/why-did-you-render',
       });
    }
    return config;
}

@jahav
Copy link

jahav commented Feb 13, 2021

@vzaidman Please update the link on the front page to the new solution, since the old one doesn't work (at least it didn't work me), or even better, incorporate the solution to the front page installation manual (CRA is a very common).

Please change the line
const isReactPreset = preset?.[1]?.runtime === 'automatic' && preset?.[1]?.development === true;
to
const isReactPreset = preset && preset[1] && preset[1].runtime === 'automatic' && preset[1].development === true;

or something similar, because the optional chaining operator didn't work for me (syntax error, so I guess parsing with older version of ECMAScript) and the solution should be copy/paste if possible for lowest common denominator.

@vzaidman
Copy link
Collaborator

@vzaidman Please update the link on the front page to the new solution, since the old one doesn't work (at least it didn't work me), or even better, incorporate the solution to the front page installation manual (CRA is a very common).

Please change the line
const isReactPreset = preset?.[1]?.runtime === 'automatic' && preset?.[1]?.development === true;
to
const isReactPreset = preset && preset[1] && preset[1].runtime === 'automatic' && preset[1].development === true;

or something similar, because the optional chaining operator didn't work for me (syntax error, so I guess parsing with older version of ECMAScript) and the solution should be copy/paste if possible for lowest common denominator.

updated. thanks

@mrudowski
Copy link

@vzaidman Thank you for
#154 (comment)

Your solution works for me
but unfortunately it looks like it disable Fast Refresh built in CRA :(

@nikitaNaredi
Copy link

nikitaNaredi commented May 2, 2021

I wanted to track my re-renders but unfortunately am missing something. I cant see the wdyr logs. Here is the installation process.

  1. npm install @welldone-software/why-did-you-render
  2. Then created wdyr.js
    import React from "react"; if (process.env.NODE_ENV === "development") { const whyDidYouRender = require("@welldone-software/why-did-you-render"); whyDidYouRender(React, { trackAllPureComponents: true, }); }
  3. Imported in index.js
  4. Changed node_modules/@babel/preset-react/lib/index.js file with
    const presetReact = require('@babel/preset-react').default; const presetCRA = require('babel-preset-react-app'); module.exports = { babel: { loaderOptions: (babelLoaderOptions, { env, paths }) => { const origBabelPresetReactAppIndex = babelLoaderOptions.presets.findIndex(preset => { return preset[0].includes('babel-preset-react-app') }) if (origBabelPresetReactAppIndex === -1) { return babelLoaderOptions; } const overridenBabelPresetReactApp = (...args) => { const babelPresetReactAppResult = presetCRA(...args); const origPresetReact = babelPresetReactAppResult.presets.find(preset => { return preset[0] === presetReact; }); Object.assign(origPresetReact[1], { importSource: '@welldone-software/why-did-you-render', }); return babelPresetReactAppResult; } babelLoaderOptions.presets[origBabelPresetReactAppIndex] = overridenBabelPresetReactApp; return babelLoaderOptions; } }, };
  5. Ran the app but nothing was visible on console of chrome.
    I have created my app using CRA. My react versions are below:
    "react": "^17.0.1", "react-dom": "^17.0.1", "react-redux": "^7.2.2", "react-router-dom": "^5.2.0", "react-scripts": "4.0.0",

Please let me know what is wrong. I really need to test my app.
Thanks in advance!

@vzaidman
Copy link
Collaborator

vzaidman commented May 2, 2021

Please let me know what is wrong. I really need to test my app.

you don't need to touch node_modules/@babel/preset-react/lib/index.js.

the config in the comment #154 (comment) is a comment for https://github.com/gsoft-inc/craco.

so install https://github.com/gsoft-inc/craco and use this code in it's config

@nikitaNaredi
Copy link

nikitaNaredi commented May 2, 2021

Thanks @vzaidman! I started seeing some logs but not all. Steps used:

  1. Installed craco
  2. Created config file and added the Support create-react-app/react-scripts 4 #154 code to it.

When I tried adding
import React from "react"; if (process.env.NODE_ENV === "development") { const whyDidYouRender = require("@welldone-software/why-did-you-render"); const ReactRedux = require("react-redux"); whyDidYouRender(React, { trackAllPureComponents: true, trackExtraHooks: [[ReactRedux, "useSelector"]], }); }

I got this error.

whyDidYouRender.js:196 Uncaught TypeError: Cannot set property useSelector of #<Object> which has only a getter at whyDidYouRender.js:196 at Array.forEach (<anonymous>) at trackHooksIfNeeded (whyDidYouRender.js:178) at whyDidYouRender (whyDidYouRender.js:282) at Module.<anonymous> (wdyr.js:6) at Module../src/wdyr.js (wdyr.js:11) at __webpack_require__ (bootstrap:851) at fn (bootstrap:150) at Module.<anonymous> (Share.js:9) at Module../src/index.js (index.js:24) at __webpack_require__ (bootstrap:851) at fn (bootstrap:150) at Object.1 (wdyr.js:11) at __webpack_require__ (bootstrap:851) at checkDeferredModules (bootstrap:45) at Array.webpackJsonpCallback [as push] (bootstrap:32) at main.chunk.js:1

 I need to track all hook(useSelector basically) re-rendering also. How to do that?

Please let me know what is wrong. I really need to test my app.

you don't need to touch node_modules/@babel/preset-react/lib/index.js.

the config in the comment #154 (comment) is a comment for https://github.com/gsoft-inc/craco.

so install https://github.com/gsoft-inc/craco and use this code in it's config

@taronaeo
Copy link

taronaeo commented May 8, 2021

Verified that @vzaidman's solution is working. (see)

Just curious. By any chance could we have either a workaround or an actual fix without craco being involved?

@vzaidman
Copy link
Collaborator

vzaidman commented May 8, 2021

Verified that @vzaidman's solution is working. (see)

Just curious. By any chance could we have either a workaround or an actual fix without craco being involved?

Great!

Regarding the workaround- It's not possible as long as we can't pass options to @babel/preset-react inside of babel-preset-react-app.

@justingrant
Copy link

Instead of relying on craco, I'd like to use patch-package to just change the code inside babel-preset-react-app.

Is this the correct code to use?

      [
        require('@babel/preset-react').default,
        {
          // Adds component stack to warning messages
          // Adds __self attribute to JSX which React will use for some warnings
          development: isEnvDevelopment || isEnvTest,
          // Will use the native built-in instead of trying to polyfill
          // behavior for any plugins that require one.
          ...(opts.runtime !== 'automatic' ? { useBuiltIns: true } : {}),

          // runtime: opts.runtime || 'classic',
          // See https://github.com/welldone-software/why-did-you-render/issues/154#issuecomment-732711993
          runtime: 'automatic',
          development: process.env.NODE_ENV === 'development',
          importSource: '@welldone-software/why-did-you-render',
        },
      ],

Here's what it will be replacing from https://github.com/facebook/create-react-app/blob/main/packages/babel-preset-react-app/create.js#L90-L101:

      [
        require('@babel/preset-react').default,
        {
          // Adds component stack to warning messages
          // Adds __self attribute to JSX which React will use for some warnings
          development: isEnvDevelopment || isEnvTest,
          // Will use the native built-in instead of trying to polyfill
          // behavior for any plugins that require one.
          ...(opts.runtime !== 'automatic' ? { useBuiltIns: true } : {}),
          runtime: opts.runtime || 'classic',
        },
      ],

@vzaidman
Copy link
Collaborator

Instead of relying on craco, I'd like to use patch-package to just change the code inside babel-preset-react-app.

Is this the correct code to use?

It seems correct. Tell us if it works, please.

@justingrant
Copy link

Instead of relying on craco, I'd like to use patch-package to just change the code inside babel-preset-react-app.
Is this the correct code to use?

It seems correct. Tell us if it works, please.

It seems to be working.

@avanderhoorn
Copy link

@justingrant To make it easier for others trying this approach out, can you reference your patch file, and the patch command you ended up writing (i.e. did you end up leveraging the Dev-only patches capability, etc).

@justingrant
Copy link

@justingrant To make it easier for others trying this approach out, can you reference your patch file, and the patch command you ended up writing (i.e. did you end up leveraging the Dev-only patches capability, etc).

Sure, here's the patch: https://gist.github.com/justingrant/f8553e52ec9ce26f15009e73f54e609e

I used just plain vanilla npx patch-package babel-preset-react-app

did you end up leveraging the Dev-only patches capability

Isn't this package is only used in development? ow would the patched code get into the production bundle?

Patching only in dev sounds like a good idea in general, but just not sure why it matters in this case.

@vzaidman
Copy link
Collaborator

vzaidman commented Sep 4, 2021

Patching only in dev sounds like a good idea in general, but just not sure why it matters in this case.

The library is not tested in prod. It also makes the bundle size bigger and most importantly may cause significant errors in certain edge cases. I wouldn't get it near production. Only turn it on here and there to debug performance.

@justingrant
Copy link

The library is not tested in prod. It also makes the bundle size bigger and most importantly may cause significant errors in certain edge cases. I wouldn't get it near production. Only turn it on here and there to debug performance.

Oh, I was only referring to the patch to babel-preset-react-app. I don't use WDYR in production.

@christo8989
Copy link

Is there a macro for why-did-you-render?

@vzaidman
Copy link
Collaborator

Is there a macro for why-did-you-render?

There's no macro. From my humble knowledge of macros, we can't implement many WDYR features with it.

Do you think there is a way?

@mrlubos
Copy link

mrlubos commented Jan 17, 2022

@vzaidman After upgrading to CRA v5, I am running into one of the flavours of the "getter" errors I've seen people post in this and other threads. I was already using the CRACO fix and don't use Redux, any idea what might be causing it?

@Isaiasg
Copy link

Isaiasg commented Feb 3, 2022

I'm having trouble making it work with create-react-app (using react-app-rewired)

I followed @Niks218 suggestion.
The setup seems to be good, but I only get it working when I use
include: [./*] option.
trackAllPureComponents: true does not seem to have any effect.

What am I doing wrong?

sandbox

@vzaidman
Copy link
Collaborator

vzaidman commented Apr 2, 2022

sandbox

The sandbox doesn't seem to work for me (problem with deps fetching). I'll try again later.

@RichardTMiles
Copy link

RichardTMiles commented Dec 23, 2022

#231
LyulyaevMaxim's comment solved my CRA, updated DEC, 2022 node v19

config-overrides.js

/* config-overrides.js */
// @link https://github.com/timarney/react-app-rewired/
const getLogger = require('webpack-log');
const rewireTypingsForCssModule = require("react-app-rewire-typings-for-css-module");
const {
    override,
    addWebpackPlugin, getBabelLoader,
} = require('customize-cra')
const log = getLogger({name: 'config-overrides.js'});
const MiniCssExtractPlugin = require('mini-css-extract-plugin') 

const {
    defaultGetLocalIdent
} = require("css-loader/dist/utils")

module.exports = override(
    addWebpackPlugin(new MiniCssExtractPlugin()),
    function (config, _env) {

        log.info('webpack env', process.env.NODE_ENV)

        // what's going on here? @link https://stackoverflow.com/questions/73551420/removing-hash-from-react-css-modules
        // noinspection JSUnusedGlobalSymbols
        config = rewireTypingsForCssModule.factory({
            modules: {
                exportLocalsConvention: 'camelCase',
                mode: 'local',
                localIdentName: 'production' === process.env.NODE_ENV ? '[hash:base64:5]' : '[path][name]__[local]--[hash:base64:5]',
                exportGlobals: true,
                getLocalIdent: (context, localIdentName, localName, options) => {

                    if (false === context.resourcePath.endsWith('/react/src/variables/bootstrap.module.scss')) {

                        localName = defaultGetLocalIdent(context, localIdentName, localName, options)
                            .replace("[local]", localName)

                    }

                    return localName;

                }

            }
        })(config); // do not pass env as it will cause the production build will not be modified

        // log.info('webpack configuration post config-overrides.js', JSON.stringify(config))

        return config;

    },
    (config) => {
        const options = getBabelLoader(config).options;

        const originalPreset = options.presets.find((preset) => preset[0].includes('babel-preset-react-app'));
        if (originalPreset) {
            Object.assign(originalPreset[1], {
                development: true,
                importSource: '@welldone-software/why-did-you-render',
            });
        }
        return config;
    }
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
workaround available A workaround is available in the issue's comments
Projects
None yet
Development

No branches or pull requests