reactjs / react-docgen

A CLI and library to extract information from React component files for documentation generation purposes.
https://react-docgen.dev
MIT License
3.65k stars 295 forks source link

Doesn't find HOCs that just wrap & re-export component #177

Open jdiefen opened 7 years ago

jdiefen commented 7 years ago

Some HOCs aren't correctly parsed when they're just calling a function on an imported component. For example, exporting a HelloButton component that just exports an imported Button component wrapped in redux connect:

hello-button.js:

// @flow
import { connect } from 'react-redux';

import { sayHello } from '../actions/hello';
import Button from '../components/Button';

const mapStateToProps = () => ({
        label: 'Say something',
});

const mapDispatchToProps = dispatch => ({
        handleClick: () => {
                dispatch(sayHello('Hi!!!'));
        },
});

// HelloButton
export default connect(mapStateToProps, mapDispatchToProps)(Button);

button.js:

// @flow
import React from 'react';
import './index.css';

type Props = {
        label: string,
        handleClick: Function
};

const Button = ({ label, handleClick }: Props) =>
        <button styleName='button' onClick={handleClick}>{label}</button>
;

export default Button;

Button is properly parsed, but HelloButton is not considered a component, even when using the findAllComponentDefinitions resolver.

fkling commented 7 years ago

react-docgen only looks at files in isolation. It doesn't know that Button inside hello-button.js is a React component.

What would you expect the result for hello-button.js to be? It shouldn't be difficult to write a custom resolver that finds connect calls.

jdiefen commented 7 years ago

Ah, that makes more sense! I thought it did (or was supposed to do) cross-file resolution too.

Writing a resolver that finds connect calls might not be too difficult...I'm honestly just hoping to find a documentation-generation tool that can parse my React/Flow code. Basically, just JSDoc + React/Flow parsing.

Thanks for the clarification :)

threehams commented 7 years ago

I'm having the same issue with a different type of HOC. Is there a way we could manually hint to the parser (through comment or code) that something returns a React component? Making exceptions for specific ones probably isn't sustainable, given the number of them out there (Radium, Aphrodite, Relay, recompose, react-redux, ...rest).

import { compose, withState, withHandlers } from "recompose";

const FixedComponent: Class<Component<Props, null, null>> = compose(
  withState("fixed", "setFixed", false),
  withHandlers({
    onEnter: props => () => props.setFixed(false),
    onLeave: props => () => props.setFixed(true),
  }),
)(Component);
fkling commented 7 years ago

@threehams: Yeah, I just recently thought about that too. Do you have something concrete in mind?

threehams commented 7 years ago

Could use jsdoc's @type. I'd be a little concerned about piggybacking on existing tags - would there be cases that work now, but would break with something like this? (I have very little experience with jsdoc so I'm not sure if there are major downsides here.)

/**
 * Fixes the component to the top of the page after scrolling past it.
 * @type React.Component
 * or
 * @type Component
 */
const FixedComponent: Class<Component<Props, null, null>> = compose(
// etc

It feels a bit strange to have to add a jsdoc tag when using Flow, but typing HOCs is very experimental right now.

FezVrasta commented 6 years ago

Hi, looks like this is a deal breaker for people using recompose to create their components.

Is there any progress on this front?

fkling commented 6 years ago

@FezVrasta: Maybe https://github.com/Jmeyering/react-docgen-annotation-resolver helps you.

The general problem is that there is no standard format for HOCs. A possible solution is to write custom resolvers and handlers for specific types of HOCs, but these have to come from the community.

If you are interested in doing that for recompose, have a look at the default resolvers and handlers, and how they work. I'm also happy to answer any questions regarding this.

FezVrasta commented 6 years ago

Isn't there any way to manually mark a function as "component"? Maybe with a comment, or a regex?

In my case I only export default components, so I would just like react-docgen to use them all..

fkling commented 6 years ago

@FezVrasta: The resolver I linked to allows you to do that.

Richacinas commented 5 years ago

@FezVrasta: The resolver I linked to allows you to do that.

Do you happen to know how to use that resolver?

I'm putting this into my plugins list in .babelrc but nothing happens:

[
    "babel-plugin-react-docgen",
    {
      "resolver": "react-docgen-annotation-resolver",
      "removeMethods": true
    }
  ]

Of course, I tagged the component I wanted to be taken into account by react-docgen like this:

const myComponent = () => (
  <h1>Component</h1>
);

/**
 * @component
 */
export default withCustomHOC(myComponent);