Git Product home page Git Product logo

welldone-software / why-did-you-render Goto Github PK

View Code? Open in Web Editor NEW
10.8K 39.0 186.0 8.51 MB

why-did-you-render by Welldone Software monkey patches React to notify you about potentially avoidable re-renders. (Works with React Native as well.)

Home Page: https://www.npmjs.com/package/@welldone-software/why-did-you-render

License: MIT License

JavaScript 97.62% TypeScript 2.35% Shell 0.03%
react component pure performance render update tool react-native purecomponent hooks-tracking

why-did-you-render's Introduction

Why Did You Render

npm version Build Status NPM Snyk Vulnerabilities for npm package Coverage Status

why-did-you-render by Welldone Software monkey patches React to notify you about potentially avoidable re-renders. (Works with React Native as well.)

For example, if you pass style={{width: '100%'}} to a big pure component it would always re-render on every element creation:

<BigListPureComponent style={{width: '100%'}}/>

It can also help you to simply track when and why a certain component re-renders.

Setup

The latest version of the library was tested (unit tests and E2E) with React@18 only. For React 17 and 16, please use version @^7.

npm install @welldone-software/why-did-you-render --save-dev

or

yarn add --dev @welldone-software/why-did-you-render

If you use the automatic JSX transformation, set the library to be the import source, and make sure preset-react is in development mode.

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

Unfortunately, the metro-react-native-babel-preset that comes with react-native out of the box does not allow you to change the options of the babel/plugin-transform-react-jsx plugin. Just add the plugin with options as listed below and start react-native packager as usual. Default env for babel is "development". If you do not use expo when working with react-native, the following method will help you.

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],

  env: {
    development: {
      plugins: [['@babel/plugin-transform-react-jsx', { runtime: 'classic' }]],
    },
  },
}

Notice: Create React App (CRA) ^4 does use the automatic JSX transformation. See the following comment on how to do this step with CRA

Create a wdyr.js file and import it as the first import in your application.

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,
  });
}

Notice: The library should NEVER be used in production because it slows down React

In Typescript, call the file wdyr.ts and add the following line to the top of the file to import the package's types:

/// <reference types="@welldone-software/why-did-you-render" />

Import wdyr as the first import (even before react-hot-loader):

index.js:

import './wdyr'; // <--- first import

import 'react-hot-loader';
import {hot} from 'react-hot-loader/root';

import React from 'react';
import ReactDOM from 'react-dom';
// ...
import {App} from './app';
// ...
const HotApp = hot(App);
// ...
ReactDOM.render(<HotApp/>, document.getElementById('root'));

If you use trackAllPureComponents like we suggest, all pure components (React.PureComponent or React.memo) will be tracked.

Otherwise, add whyDidYouRender = true to component classes/functions you want to track. (f.e Component.whyDidYouRender = true)

More information about what is tracked can be found in Tracking Components.

Can't see any WDYR logs? Check out the troubleshooting section or search in the issues.

Custom Hooks

Also, tracking custom hooks is possible by using trackExtraHooks. For example if you want to track useSelector from React Redux:

wdyr.js:

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']
    ]
  });
}

Notice that there's currently a problem with rewriting exports of imported files in webpack. A quick workaround can help with it: #85 - trackExtraHooks cannot set property.

Read More

Integration With Other Libraries

Sandbox

You can test the library in the official sandbox.

And another official sandbox with hooks tracking

Tracking Components

You can track all pure components (React.PureComponent or React.memo) using the trackAllPureComponents: true option.

You can also manually track any component you want by setting whyDidYouRender on them like this:

class BigList extends React.Component {
  static whyDidYouRender = true
  render(){
    return (
      //some heavy render you want to ensure doesn't happen if its not necessary
    )
  }
}

Or for functional components:

const BigListPureComponent = props => (
  <div>
    //some heavy component you want to ensure doesn't happen if its not necessary
  </div>
)
BigListPureComponent.whyDidYouRender = true

You can also pass an object to specify more advanced tracking settings:

EnhancedMenu.whyDidYouRender = {
  logOnDifferentValues: true,
  customName: 'Menu'
}
  • logOnDifferentValues:

    Normally, only re-renders that are caused by equal values in props / state trigger notifications:

    render(<Menu a={1}/>)
    render(<Menu a={1}/>)

    This option will trigger notifications even if they occurred because of different props / state (Thus, because of "legit" re-renders):

    render(<Menu a={1}/>)
    render(<Menu a={2}/>)
  • customName:

    Sometimes the name of the component can be missing or very inconvenient. For example:

    withPropsOnChange(withPropsOnChange(withStateHandlers(withPropsOnChange(withState(withPropsOnChange(lifecycle(withPropsOnChange(withPropsOnChange(onlyUpdateForKeys(LoadNamespace(Connect(withState(withState(withPropsOnChange(lifecycle(withPropsOnChange(withHandlers(withHandlers(withHandlers(withHandlers(Connect(lifecycle(Menu)))))))))))))))))))))))

Options

Optionally you can pass in options as the second parameter. The following options are available:

  • include: [RegExp, ...] (null by default)
  • exclude: [RegExp, ...] (null by default)
  • trackAllPureComponents: false
  • trackHooks: true
  • trackExtraHooks: []
  • logOwnerReasons: true
  • logOnDifferentValues: false
  • hotReloadBufferMs: 500
  • onlyLogs: false
  • collapseGroups: false
  • titleColor
  • diffNameColor
  • diffPathColor
  • notifier: ({Component, displayName, hookName, prevProps, prevState, prevHook, nextProps, nextState, nextHook, reason, options, ownerDataMap}) => void
  • getAdditionalOwnerData: (element) => {...}

include / exclude

(default: null)

You can include or exclude tracking of components by their displayName using the include and exclude options.

For example, the following code is used to track all redundant re-renders that are caused by older React-Redux:

whyDidYouRender(React, { include: [/^ConnectFunction/] });

Notice: exclude takes priority over both include and manually set whyDidYouRender =

trackAllPureComponents

(default: false)

You can track all pure components (both React.memo and React.PureComponent components)

Notice: You can exclude the tracking of any specific component with whyDidYouRender = false

trackHooks

(default: true)

You can turn off tracking of hooks changes.

Understand and fix hook issues.

trackExtraHooks

(default: [])

Track custom hooks:

whyDidYouRender(React, {
  trackExtraHooks: [
    // notice that 'useSelector' is a named export
    [ReactRedux, 'useSelector'],
  ]
});

There is currently a problem with rewriting exports of imported files in webpack. A workaround is available here: #85 - trackExtraHooks cannot set property

logOwnerReasons

(default: true)

One way of fixing re-render issues is preventing the component's owner from re-rendering.

This option is true by default and it lets you view the reasons why an owner component re-renders.

demo

logOnDifferentValues

(default: false)

Normally, you only want logs about component re-renders when they could have been avoided.

With this option, it is possible to track all re-renders.

For example:

render(<BigListPureComponent a={1}/>)
render(<BigListPureComponent a={2}/>)
// will only log if you use {logOnDifferentValues: true}

hotReloadBufferMs

(default: 500)

Time in milliseconds to ignore updates after a hot reload is detected.

When a hot reload is detected, we ignore all updates for hotReloadBufferMs to not spam the console.

onlyLogs

(default: false)

If you don't want to use console.group to group logs you can print them as simple logs.

collapseGroups

(default: false)

Grouped logs can be collapsed.

titleColor / diffNameColor / diffPathColor

(default titleColor: '#058')
(default diffNameColor: 'blue')
(default diffPathColor: 'red')

Controls the colors used in the console notifications

notifier

(default: defaultNotifier that is exposed from the library)

You can create a custom notifier if the default one does not suite your needs.

getAdditionalOwnerData

(default: undefined)

You can provide a function that harvests additional data from the original react element. The object returned from this function will be added to the ownerDataMap which can be accessed later within your notifier function override.

Troubleshooting

No tracking

  • If you are in production, WDYR is probably disabled.
  • Maybe no component is tracked
  • If you only track pure components using trackAllPureComponents: true then you would only track either (React.PureComponent or React.memo), maybe none of your components are pure so none of them will get tracked.
  • Maybe you have no issues
    • Try causing an issue by temporary rendering the whole app twice in it's entry point:

      index.js:

      const HotApp = hot(App);
      HotApp.whyDidYouRender = true;
      ReactDOM.render(<HotApp/>, document.getElementById('root'));
      ReactDOM.render(<HotApp/>, document.getElementById('root'));

Custom Hooks tracking (like useSelector)

There's currently a problem with rewriting exports of imported files in webpack. A quick workaround can help with it: #85 - trackExtraHooks cannot set property.

React-Redux connect HOC is spamming the console

Since connect hoists statics, if you add WDYR to the inner component, it is also added to the HOC component where complex hooks are running.

To fix this, add the whyDidYouRender = true static to a component after the connect:

  const SimpleComponent = ({a}) => <div data-testid="foo">{a.b}</div>)
  // not before the connect:
  // SimpleComponent.whyDidYouRender = true
  const ConnectedSimpleComponent = connect(
    state => ({a: state.a})
  )(SimpleComponent)
  // after the connect:
  SimpleComponent.whyDidYouRender = true

Sourcemaps

To see the library's sourcemaps use the source-map-loader.

Credit

Inspired by the following previous work:

License

This library is MIT licensed.

why-did-you-render's People

Contributors

alex-page avatar andersdjohnson avatar barakyosi avatar bduff9 avatar busybox11 avatar dependabot[bot] avatar ereddrex avatar genhain avatar hrazmsft avatar hypnosphi avatar iamakulov avatar igorrmotta avatar jared-hexagon avatar jfrumar-infinitusai avatar jnachtigall avatar joelbrenstrum avatar joeyparis avatar jussikinnula avatar kostasx avatar leroydev avatar lpmi-13 avatar mbman avatar mostafah avatar nirvdrum avatar noramens avatar oliverjash avatar sergeylaptev avatar theehsansarshar avatar vzaidman avatar xepozz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

why-did-you-render's Issues

[Question] ability to add to all components?

First, thanks for such an amazing tool with hooks support. Incredibly helpful.

We have over 100 components in our project, and it will take a long time to manually add Component.whyDidYouRender = true; in every single file.

Is there a way to attach to everything? I see the [include] option, but we are currently not using DisplayName - so we would still have to touch every file.

Thanks for the help.

[NEXT.JS] TypeError: Object.defineProperty called on non-object

I'm facing issues using NextJs. I'd tried different ways to do it but always the same results.
Even in build mode and dev mode, the issue is the same just change the file names because of the hashes.

I import React globally, so I don't have to put: class NameClass extends React.Component/

I also tried to import in different scenarios mentioned in the doc and also using require and import, but didn't work.

I don't have any modifications in babel plugins and etc, but NextJS have their own.
This is preset:
https://github.com/zeit/next.js/blob/canary/packages/next/build/babel/preset.ts

I think that looking into this you can assume if any plugin is causing this issue.

In this screenshot is the issue when I start a server with builded version:
Screen Shot 2019-04-10 at 10 10 41

Clarity about React Native

Does this library work with React Native? If so, are there any additional steps? In any case, updating README would be useful.

PS: Thanks for the great work!

Breaks child.type comparisons

This will always return an empty array when activated:

const MenuItem = React.forwardRef((props, ref) => { return <div>Example</div> })
MenuItem.whyDidYouRender = true

let focusableChildrenTypes = [MenuItem]

let isFocusableChildType = (child): child is React.ReactElement =>
  focusableChildrenTypes.includes(child.type)
let getFocusableMenuChildren = (children) => {
  let focusable: any[] = []
  React.Children.forEach(children, (child) => {
    console.log('Checking focusable', child.type === MenuItem)
    if (isFocusableChildType(child)) focusable.push(child)
  })
  return focusable
}

Is there a way to get this module to work with child.type comparisons? Thank you.

Uncaught RangeError: Maximum call stack size exceeded

On this commit, as soon as I use even only try to start my app with /registration, it gets into an infinite loop. Edit: the other, technically working, routes /login, /factories and /mines also crash.

The above error occurred in the <Registration> component:
    in Registration (created by Context.Consumer)
    in Route (at Routes.tsx:28)
    in Switch (at Routes.tsx:19)
    in main (at Routes.tsx:18)
    in Routes (at RHelper.tsx:17)
    in RHelper (at src/index.tsx:30)
    in Router (created by ConnectedRouter)
    in ConnectedRouter (created by Context.Consumer)
    in ConnectedRouterWithContext (created by Context.Consumer)
    in Connect(ConnectedRouterWithContext) (at src/index.tsx:29)
    in PersistGate (at src/index.tsx:28)
    in Provider (at src/index.tsx:27)

It's stuck on useState(false)
Bildschirmfoto 2019-04-05 um 20 06 17

one level up:
Bildschirmfoto 2019-04-05 um 20 07 24

Did I miss anything concerning documentation or is it the mix of tools I use?

What to do with react-router?

Many times I have problem with <Link ... /> and <Route ... /> maybe with something like:

<Route
  path={`${match.url}/:player_id`}
  children={props => (
    <PlayerDetail {...props} teamId={match.params.team_id} />
  )}
/>
Route
whyDidYouRender.min.js:1185 {Route: ƒ} "Re-rendered because of props changes:"
whyDidYouRender.min.js:1190 props.children
whyDidYouRender.min.js:1190 different functions with the same name.
whyDidYouRender.min.js:1190 {prev children: ƒ} "!==" {next children: ƒ}

Can we make a Wiki page (or you have already planned a future article) just for react-router?

Functions in useContext API

Hello,

I have a Context API component to show/hide a modal. I'm using something very similar to the Toast example from here: Ponyfoo Toast

WDYR gives me this error:
Captura de Tela 2019-06-03 às 10 39 51

How can I avoid this re-render? Or it is normal react behavior?

All functions are created using useCallback, I've already tried adding memo to both the <Header />(the component that I tested WDYR) and the <Context.Provider />

Any ideas?

Thanks in advance

False Positive

Getting warning:
"Re-rendered because the state object itself changed but it's values are all equal." "This usually means this component called setState when no changes in it's state actually occurred." "more info at http://bit.ly/wdyr02"
prev state: Object {} !== Object {} :next state

I added a console log of the props, and it can be seen that its only rendering once per prop change.

The component:

class MyComp extends PureComponent {
    static whyDidYouRender = true;

    render() {
        console.log('rerendered', this.props.downloadProgress);
        return (
            <View/>
        );
    }
}

const mapStateToProps = state => ({
    downloadProgress: getDownloadProgress(state),
});

export default connect(mapStateToProps)(MyComp);

Every time the prop changes causing a rerender I get the warning above that the warning is unnecessary, which is false.

It may also be worth noting that this component is opened/hosted via react-navigation.

No logs

I've installed this package and I'm using it on my react native project like this:

if (process.env.NODE_ENV !== 'production') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React);
}

But I get no log/error at all.

Do I need to do anything after install package?

"@welldone-software/why-did-you-render": "^3.0.0-beta.5",
"react": "16.8.3"
"react-native": "0.59.2"

"different functions with the same name", but I'm using useCallback.

I've tried posting this on StackOverflow with no response was hoping to get some more detail here. I recreated a very simple project to replicate what's going on in a bigger project of mine. https://github.com/DaleSalcedo/PropFunctionProblem. In my App.js I'm passing down this function:

const modifyPlayer = React.useCallback((playerId, propertyName, value) => {
    const playerCopy = {...playerDict[playerId]};
    playerCopy[propertyName] = value;
    const playerDictCopy = {
      ...playerDict,
      [playerId]: playerCopy
    };
    setPlayerDict(playerDictCopy);  // useState function
  }
  ,[playerDict]);

I pass that down 2 components as props and use it here:

export const Player = ({player, modifyPlayer}) => {
  const handleOnChange = React.useCallback((event) => {
    modifyPlayer(player.id, event.target.name, event.target.value);
  }, [player, modifyPlayer]);

  return (
    <div>
      <input type={"text"} name={"firstName"} value={player.firstName} onChange={handleOnChange}/>
      <input type={"text"} name={"lastName"} value={player.lastName} onChange={handleOnChange}/>
    </div>
  );
};

I'm not sure why it's giving me that message "different functions with the same name" for "modifyPlayer" function when I used "useCallback".

Is this a bug or can someone explain what's going on?

react-i18next re-render

It seems that "react-i18next" triggers a re-render of the component.
What step do you think is the best course of action?

Screenshot 2019-06-06 at 01 10 24

Recompose "Pure" wipes logs

Hi

I used this lib and its great. So I started to fixing my rerending problems. I added Recompose's Pure() HOC to check how it goes. My rerenders still occur, but the logs are missing.

Does this lib has debugger?

React ver: 16.8.6
why-did-you-render ver: 3.2.1

No any output in console. How I can check if the library is working?

Setup code:
import whyDidYouRender from '@welldone-software/why-did-you-render'
whyDidYouRender(React)

Снимок экрана 2019-07-03 в 16 57 24

Component code:
Снимок экрана 2019-07-03 в 16 58 17

create-react-app React performance with SVG as ReactComponent and eslint with React.memo missing display name

I'm using this component in a create-react-app app:

import { ReactComponent as ProfileIcon } from "./icons/profile.svg";

...
render(){
  <ProfileIcon {...props}/>
}

Using also why-did-you-render (https://github.com/welldone-software/why-did-you-render) I got this warning:

SvgProfileIcon
whyDidYouRender.min.js:1191 {SvgProfileIcon: ƒ} "Re-rendered because the props object itself changed but it's values are all equal." "This could of been avoided by making the component pure, or by preventing it's father from re-rendering." "more info at http://bit.ly/wdyr02"
whyDidYouRender.min.js:1191 prev props: {svgRef: null, className: "icon", height: "24", width: "24"} !== {svgRef: null, className: "icon", height: "24", width: "24"} :next props

So I made a custom PureComponent like this:

import React from "react";

export default WrappedComponent =>
  React.memo(props => <WrappedComponent {...props} />, () => true);

FIRST QUESTION: Is this performances-correct?

I'm using it like this:

import { ReactComponent as ProfileIcon } from "./icons/profile.svg";

import PureComponent from "./PureComponent";

const PureProfileIcon = PureComponent(ProfileIcon);

...
render(){
  <PureProfileIcon {...props}/>
}

SECOND QUESTION: Can I avoid this component at all using React.memo (or something else) differently?

Now eslint is complaining about:

Component definition is missing display name eslint(react/display-name)

THIRD QUESTION: How can I fix this?

There was no `forceUpdate`, but getting that as error message

I'm seeing lots of:
Re-rendered although props and state objects are the same. This usually means there was a call to this.forceUpdate() inside the component.

However, nowhere in those components is there a forceUpdate(). So what could that mean?

Unable to get it work on Expo

Hi,
today im trying to work in expo and unable to get it work, something missing?

here the code:

import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';

if (__DEV__) {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React);
}

console.log('Example log')

export default class App extends React.Component {
  static whyDidYouRender = true
  constructor(props) {
    super(props);
    this.state = {
      visible: false
    };
  }
  render() {
    const { visible } = this.state;
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={() => this.setState({ visible: !visible })}>
          <Text>{visible ? 'Hide' : 'Show'}</Text>
        </TouchableOpacity>
        { visible === true && <Text style={{ marginTop: 16 }}>Hidden content</Text> }
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ecf0f1',
    padding: 8,
  }
});

and here the link to snack:

https://snack.expo.io/@outatime/why-did-you-render

Thanks !!!

React Native 0.60.5 fails to launch and memory goes through the roof

Seems like this library works when running React Native but only when the debugger is attached - which I think implies it has to do with the JavaScript engine (when attaching the debugger it runs V8). It goes for both iOS and Android - on the device and simulator.

It launches the app - succeeds in running decent amounts of JavaScript code i can tell from the logs - but fails to render. Memory consumption goes through the roof during this process as well (after about a minute my app was consuming almost 10gb of memory).

It doesn't render the library completely unusable with RN but it bugs me to have to manually comment the whole library out when I want to run it without an attached debugger - which is usually the case since it has some heavy performance implications.

Bug when component is wrapped in memo and forwardRef

Issue

When a React functional component is wrapped in both React.memo and React.forwardRef with why-did-you-render enabled you get this error:

ncaught TypeError: WrappedFunctionalComponent is not a function
    at WDYRWrappedByMemoFunctionalComponent (whyDidYouRender.js:740)
    at renderWithHooks (react-dom.development.js:13449)
    at mountIndeterminateComponent (react-dom.development.js:15605)
    at beginWork (react-dom.development.js:16238)
    at performUnitOfWork (react-dom.development.js:20279)
    at workLoop (react-dom.development.js:20320)
    at renderRoot (react-dom.development.js:20400)
    at performWorkOnRoot (react-dom.development.js:21357)
    at performWork (react-dom.development.js:21267)
    at performSyncWork (react-dom.development.js:21241)
    at requestWork (react-dom.development.js:21096)
    at scheduleWork (react-dom.development.js:20909)
    at scheduleRootUpdate (react-dom.development.js:21604)
    at updateContainerAtExpirationTime (react-dom.development.js:21630)
    at updateContainer (react-dom.development.js:21698)
    at ReactRoot.push../node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render (react-dom.development.js:22011)
    at react-dom.development.js:22163
    at unbatchedUpdates (react-dom.development.js:21486)
    at legacyRenderSubtreeIntoContainer (react-dom.development.js:22159)
    at Object.render (react-dom.development.js:22234)
    at Module../src/index.js (index.js:7)
    at __webpack_require__ (bootstrap:781)
    at fn (bootstrap:149)
    at Object.0 (serviceWorker.js:135)
    at __webpack_require__ (bootstrap:781)
    at checkDeferredModules (bootstrap:45)
    at Array.webpackJsonpCallback [as push] (bootstrap:32)
    at main.chunk.js:1

or when minified:

Uncaught TypeError: o is not a function

Repro

https://github.com/jared-hexagon/whydidyourender-memo-forwardref

https://codesandbox.io/s/github/jared-hexagon/whydidyourender-memo-forwardref

Details

Does this work with expo RN apps?

Hi.
I've followed the setup as per the docs and tried some other variations, all to no avail.

Any reason why this shouldn't work?

Here's my App.tsx file:

import React from 'react'
import { StyleSheet, Text, View, Button } from 'react-native'

if (process.env.NODE_ENV !== 'production') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render/dist/no-classes-transpile/umd/whyDidYouRender.min.js')
  whyDidYouRender(React)
}

const { useState } = React

const TestComp = () => {
  const [visible, setVisible] = useState(false)
  return (
    <View>
      {visible && (
        <View>
          <Text>Is Visible</Text>
        </View>
      )}
      <Button
        title="Press Me!"
        onPress={() => {
          setVisible(true)
        }}
      />
    </View>
  )
}

TestComp.whyDidYouRender = true

const App = () => {
  return (
    <View style={styles.container}>
      <TestComp />
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
})

export default App


Usage with multiple instances of a component

Hi,

it seems the library in designed around 'big' components that exists only once in the tree. Are there any plans to support multiple instances of functional components?

Best regards
Marco

Hooks are not supported

functional components with hooks work as expected regarding their props.

but we dont have a way right now to track state changes that are caused by the "useState" hook for example even if they trigger a render with similar state.

3RD party debugging(Reactotron via realm.js edition)

I recently made the pull request for #64 and it was not out of philanthropy and more necessity.
I am on React Native using realm.js and anyone who has tried using realm on React Native knows that it has some issues.

After browsing solutions in the issues of Realm, you will come across people mentioning Reactotron. It works wonderfully with a few tweaks.

The issue is that the Reactotron is, it does not seem to have an equivalent function of console.group() and console.groupEnd() which the default notifier of this library uses.

Now of course this library allows you you to pass in a notifier options to handle it yourself, but it has quite a fair amount of data returned to you which can be overwhelming initially for you to figure out what is going on. I have created a gist which i hope has similar behaviour to the default console. Some feedback/corrections would be appreciated as i think this is a fantastic tool but anyone using a similar stack to me might not find it easy to use.

Another thing i have noticed is that React.memo() seems to suppress why did you render logs but was supposed to be fixed in v3.3.4 as the closing comment in #50 says

Kind Of false positive when using `React.StrictMode`

When running whyDidYouRender on a tree rendered inside StrictMode, e.g.

<React.StrictMode>
  <MyComponent />
</React.StrictMode>

I get this after any legitimate (e.g. props changed) update:

Re-rendered although props and state objects are the same. This usually means there was a call to this.forceUpdate() inside the component.

This happens because StrictMode renders each components twice to catch bugs related to side effects in render. I wonder if those second renders could be filtered out somehow

Export default notifier?

I would like to keep the defaultNotifier but extend some of the values, such as displayName with some info from the props, to make it easier to identify the troublesome component.

An error is thrown if `renderToStaticMarkup` is used somewhere

To reproduce, add anywhere on top-level of your app:

class MyComponent extends React.Component {
  render() {
    return null
  }
}
const string = React.renderToStaticMarkup(<MyComponent/>);

Error in console:

Uncaught TypeError: Cannot read property 'mode' of undefined
    at whyDidYouRender.min.js:8
    at f.value (whyDidYouRender.min.js:8)
    at processChild (react-dom-server.browser.development.js:2958)
    at resolve (react-dom-server.browser.development.js:2811)
    at ReactDOMServerRenderer.render (react-dom-server.browser.development.js:3201)
    at ReactDOMServerRenderer.read (react-dom-server.browser.development.js:3160)
    at renderToStaticMarkup (react-dom-server.browser.development.js:3660)

Looks like renderToStaticMarkup doesn't set _reactInternalFiber property on component instances

Here's my usecase for renderToStaticMarkup: in my app, there are only some parts rendered by React, while other are rendered by server templates. To use third-party (BitBucket) svg icon in non-React parts of the app, I convert a React component from Atlaskit library to static svg string with renderToStaticMarkup, and then inject it in a CSS rule.

Include original error in log output

Issue

When an error is caught and a message is logged to the console to report the error and has details about the component etc. the actual error being thrown is not logged:

Screen Shot 2019-06-17 at 11 28 01 am

This is not very helpful. You have to add a breakpoint yourself.

Solution

Include the original error as a property in the object that is logged.

An error occures when the package is imported via dynamic import

Hello!

I faced a problem playing around with React Hooks and this package.
When dynamic import of the package is used like that:

import("@welldone-software/why-did-you-render").then(whyDidYouRender => {
  whyDidYouRender(React, {
    onlyLogs: true,
    titleColor: "green",
    diffNameColor: "darkturquoise"
  });
});

the following error occurs:

proxyConsole.js:72 Warning: React has detected a change in the order of Hooks called by Example. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://fb.me/rules-of-hooks

   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. undefined                  useRef
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    in Example (at src/index.js:19)
    in App (at src/index.js:23)
console.<computed> @ proxyConsole.js:72
i.<computed> @ index.js:27
r @ backend.js:6529
warningWithoutStack @ react-dom.development.js:506
warning @ react-dom.development.js:2628
warnOnHookMismatchInDev @ react-dom.development.js:12860
updateHookTypesDev @ react-dom.development.js:12826
useRef @ react-dom.development.js:13834
useRef @ react.development.js:1472
ko @ whyDidYouRender.min.js:8
eval @ whyDidYouRender.min.js:8
useState @ react.development.js:1462
Example @ index.js? [sm]:4
renderWithHooks @ react-dom.development.js:12938
updateFunctionComponent @ react-dom.development.js:14627
beginWork @ react-dom.development.js:15637
performUnitOfWork @ react-dom.development.js:19312
workLoop @ react-dom.development.js:19352
renderRoot @ react-dom.development.js:19435
performWorkOnRoot @ react-dom.development.js:20342
performWork @ react-dom.development.js:20254
performSyncWork @ react-dom.development.js:20228
interactiveUpdates$1 @ react-dom.development.js:20495
interactiveUpdates @ react-dom.development.js:2170
dispatchInteractiveEvent @ react-dom.development.js:4882
react-dom.development.js:55 Uncaught Invariant Violation: Rendered more hooks than during the previous render.
    at invariant (https://jdq0k.csb.app/node_modules/react-dom/cjs/react-dom.development.js:55:15)
    at updateWorkInProgressHook (https://jdq0k.csb.app/node_modules/react-dom/cjs/react-dom.development.js:13092:35)
    at updateRef (https://jdq0k.csb.app/node_modules/react-dom/cjs/react-dom.development.js:13334:14)

I created an example to show the problem.

If you'll try to change something in the code, it can start working at some point, but if you reload the page, the issue will occure again (I think it is related to CodesandBox work).

Also, I have a project on my local machine with the same package version. When using dynamic import I can see the following error:
image
because it is an object with a key "default":
image
and I have to use destructuring to get it:

import("@welldone-software/why-did-you-render").then(({ default: whyDidYouRender }) => {

Don't know why, but in the sandbox the function "To" is taken by default and destructuring is not needed.

I'd like to know, first, how to use dynamic import with the package and with React Hooks (it would be better do not to make the bundle bigger using regular import), second, why the parameter that is taken from the package can be the function itself or the object with "default" key, why are they different. Is that a problem with React support of this feature?

Thanks in advance.

Scala.Js compatibility

Hi, this might be outside the scope of your intended use-cases, but I'm trying to transcompile javascript using ScalaJs and was wondering if you might be able to advise on an error I'm getting when using why-did-you-render in my React 16.8.4 application:

react-dom.development.js?4646:20312 Uncaught TypeError: t.include.some is not a function
    at eval (_freeGlobal.js?7bdd:2)
    at ro (_freeGlobal.js?7bdd:2)
    at ko (_freeGlobal.js?7bdd:2)
    at Object.eval [as useState] (_freeGlobal.js?7bdd:2)
    at Object.useState (react.development.js?9c48:1462)
    at client-fastopt.js:27081
    at $c_sjsr_AnonFunction1.apply__O__O (client-fastopt.js:25930)

Here is relevant info from errorInfo object that gets printed when tries to work on my main functional component:

displayName: "main "
options: include: ƒ (arg1) {
arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.remoteFunction (<anonymous>:2:14)]
caller: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.remoteFunction (<anonymous>:2:14)]
length:1
name:""
}

I am calling why-did-you-render with include: /.*/ to avoid polluting my code as described in the docs.

class constructors must be invoked with |new|

Trying to implement base functionality on our existing React app.

Getting

class constructors must be invoked with |new|

for any component I add the static property to.

It looks like this is a common issue across this library and the predecessors, but I was under the impression this library had it solved, so I must be missing something.

I've attached the library via NPM as well as manually adding the src to see if I could narrow it down. Best I can tell the error is thrown here (line 505):
_this = _possibleConstructorReturn(this, _getPrototypeOf(WDYRPatchedClassComponent).call(this, props, context));

Excluding markup, my component looks like this:
`class Home extends React.Component {

constructor(props) {
super(props);
}
static whyDidYouRender = true;
render(){
...
}
export default Home;
`

react-redux 7 uses useMemo, all Connected components complaining

Great job on the new release!
I just updated react-redux to 7.0.1 and WDYR is complaining about every single one. How should we handle this?

Connect(Header)
whyDidYouRender.min.js:8 {Connect(Header): ƒ} "Re-rendered because of hook changes:"
whyDidYouRender.min.js:8 hook useMemo 
whyDidYouRender.min.js:8 different React elements with the same displayName. (more info at http://bit.ly/wdyr3)
whyDidYouRender.min.js:8 {"prev ": {…}} "!==" {"next ": {…}}

browserlist, TypeError: Class constructor "Component" cannot be invoked without 'new'

TypeError: Class constructor "Component" cannot be invoked without 'new'

Using the create-react-app 3, why-did-you-render: v3.0.6 and this import:

const whyDidYouRender = require('@welldone-software/why-did-you-render/dist/no-classes-transpile/umd/whyDidYouRender.min.js')

why-did-you-render: v3.0.6

My browserlist:

  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }

can't work with React.memo

I've created a memo Component, but it's no output in my console.

when I remove memo function in my component, it works.

Is it because the why-did-you-render does not support React.memo?

TypeError: Cannot read property '_prevProps' of undefined

I'm trying to start wide then narrow it down - I have a large set of components involved in a complex form and going component by component at this point to turn on would be painful. I see Consumer and Provider types are failing with the cannot be invoked without 'new' error, no problem anyway, I am not interested in them. But I get to a point where I have no info on what to ignore to continue.

if (process.env.NODE_ENV === 'development') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render')
  whyDidYouRender(React, {
    exclude: [/Consumer|Provider|AutoSignout/],
    include: [/^.*/],
  })
}
whyDidYouRender.min.js:8 Uncaught (in promise) TypeError: Cannot read property '_prevProps' of undefined
    at value (whyDidYouRender.min.js:8)
    at f.o.render (whyDidYouRender.min.js:8)
    at createElements.js:38

Any thoughts on determining the offending displayName here?

Doesn't show anything in console

I don't know what I'm doing wrong, but nothing is shown in the console.

This is how I set it up:
I execute whyDidYouRender in index.tsx like this:

if (process.env.NODE_ENV !== 'production') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React);
}

ReactDOM.render(<App />, document.getElementById('root'));

And I added the static property whyDidYouRender to App.tsx
App.whyDidYouRender = true;

I don't know if I missing something

Higher-order components from react redux router "withRouter" seem to be ignored

[email protected]
[email protected]
[email protected]
[email protected]

I am attempting to use why-did-you-render to debug some of my route components. The main route container is calling the Search component like so:

...
<Switch>
  <Route exact path="/">
     <Redirect to="/search" />
  </Route>
  <Route path="/search" component={Search} />
  <Route path="/project" component={Project} />
</Switch>
...

and the Search component is being exported using the withRouter HOC from react-router-dom like so:

...
export default withRouter(Search)

As far as I can tell, it is not currently possible to debug any component that has been wrapped with the withRouter HOC.

Eslint plugin

Just an idea - create an eslint plugin that helps users prevent common mistakes that cause unnecessary rerenders like <Component style={{ background: 'black' />.

React.useRef is returning empty string

Uncaught TypeError: Cannot create property 'current' on string ''
    at trackHookChanges (whyDidYouRender.js:149)
    at Object.useMemo (whyDidYouRender.js:260)
    at useMemo (react.development.js:1525)
    at TakeNotes (index.tsx:18)

Screenshot 2019-06-24 at 12 55 55

I'm getting this fatal error while running .whyDidYouRender on one of my React components.

wrong message when useMemo re-renders

In the following case:

const A = () => {
   const obj = useMemo(() => ({
     // something big
  }), [a, b, c, d, e]);

 return <Child  obj={obj} />;
}

when the hook useMemo re-creates obj even if it's deps deep equals, the message says A was re-rendered because if it, where in reality useMemo re-generated obj without a reason, but A is not re-rendered because of this.

More info about 'different functions with the same name'

Hello,

Love this plugin but the information in the readme and in the blog post about 'different functions with the same name' is really sparse.

Can you explain what this means a bit more in the docs and how to fix it?

I'm getting these warnings a lot and since the functions are the same (not different functions) it's hard to figure out what to do about it.

A little more information about this particular warning would be super helpful.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.