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

Doesn’t remove propTypes in certain situations #196

Open
jaydenseric opened this issue Sep 16, 2020 · 2 comments · Fixed by #199
Open

Doesn’t remove propTypes in certain situations #196

jaydenseric opened this issue Sep 16, 2020 · 2 comments · Fixed by #199

Comments

@jaydenseric
Copy link

jaydenseric commented Sep 16, 2020

For all of the following examples, propTypes are incorrectly not removed:

import PropTypes from 'prop-types';

export default function Foo({ children }) {
  return children();
}

Foo.propTypes = {
  children: PropTypes.func.isRequired,
};

Babel REPL

import PropTypes from 'prop-types';

export default function Foo({ children }) {
  return children;
}

Foo.propTypes = {
  children: PropTypes.node,
};
import PropTypes from 'prop-types';

export default function Foo({ children }) {
  return <>{children}</>;
}

Foo.propTypes = {
  children: PropTypes.node,
};

While this works:

import PropTypes from 'prop-types';

export default function Foo({ children }) {
  return <div>{children}</div>;
}

Foo.propTypes = {
  children: PropTypes.node,
};
@jaydenseric
Copy link
Author

These situations may look edge case, but really they come up fairly often - maybe 5% of our project components.

Some examples:

import PropTypes from 'prop-types';
import { useEffect } from 'react';
import TagManager from 'react-gtm-module';

export default function GTMEventViewSearchResults({ categoryId }) {
  useEffect(() => {
    if (process.env.GTM_ID && categoryId)
      TagManager.dataLayer({
        dataLayer: {
          event: 'view_search_results',
          id: categoryId,
        },
      });
  }, [categoryId]);

  return null;
}

GTMEventViewSearchResults.propTypes = {
  categoryId: PropTypes.string.isRequired,
};
import { MapContext } from '@urbica/react-map-gl';
import PropTypes from 'prop-types';
import { useContext } from 'react';
import useSupercluster from 'use-supercluster';

/**
 * An alternative to the [`@urbica/react-map-gl-cluster`](https://github.com/urbica/react-map-gl-cluster)
 * component [`Cluster`](https://urbica.github.io/react-map-gl-cluster/#/Cluster).
 * @param {object} options Options.
 * @param {Array<object>} options.points Supercluster points; an array of [GeoJSON Feature](https://tools.ietf.org/html/rfc7946#section-3.2) objects. Each feature’s `geometry` must be a [GeoJSON Point](https://tools.ietf.org/html/rfc7946#section-3.1.2).
 * @param {object} [options.options] Supercluster options.
 * @param {Function} options.children React children render function.
 * @returns {ReactNode} React children containing map markers.
 * @see [GitHub issue for `Cluster` children not re-rendering on state updates](https://github.com/urbica/react-map-gl-cluster/issues/31).
 */
export default function MapCluster({ points, options, children }) {
  const map = useContext(MapContext);
  const { supercluster, clusters } = useSupercluster({
    points,
    bounds: map.getBounds().toArray().flat(),
    zoom: map.getZoom(),
    options,
  });

  return children(clusters, supercluster);
}

MapCluster.propTypes = {
  points: PropTypes.arrayOf(
    PropTypes.exact({
      type: PropTypes.oneOf(['Feature']).isRequired,
      geometry: PropTypes.exact({
        type: PropTypes.oneOf(['Point']).isRequired,
        coordinates: PropTypes.arrayOf(PropTypes.number.isRequired).isRequired,
      }),
      properties: PropTypes.object,
    })
  ),
  options: PropTypes.object,
  children: PropTypes.func.isRequired,
};

@GalGrigoryeva
Copy link

GalGrigoryeva commented Jun 9, 2021

I have one more example when propTypes are not removed:

import _RawContent from 'some-external-lib';

const RawContent = React.memo(props => (
  <_RawContent {...props}>
    {props.children}
  </_RawContent>
));

RawContent.propTypes = {
  children: _RawContent.propTypes.children,
};

export { RawContent };

But if I rewrite this functional component adding return

...
const RawContent = React.memo(props => {
  return (
    <_RawContent {...props}>
      {props.children}
    </_RawContent>
  );
});

...

or doing something like that

...
const RawContentComponent = props => (
  <_RawContent {...props}>
    {props.children}
  </_RawContent>
);

RawContentComponent.propTypes = {
  children: _RawContent.propTypes.children,
};

export const RawContent = React.memo(RawContentComponent);

or even changing named export into default

...

const RawContent = props => (
  <_RawContent {...props}>
    {props.children}
  </_RawContent>
);

RawContent.propTypes = {
  children: _RawContent.propTypes.children,
};

export default React.memo(RawContent);

everything works fine.

We discovered this problem only because of this line children: _RawContent.propTypes.children, as we got the error
raw-content.jsx:14 Uncaught TypeError: Cannot read property 'children' of undefined.

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

Successfully merging a pull request may close this issue.

3 participants