React-styleguidist: No suitable component definition found

Created on 14 Nov 2015  路  27Comments  路  Source: styleguidist/react-styleguidist

image

I know this is not an issue specifically with react-styleguidist, for example: https://github.com/reactjs/react-docgen/issues/29

This happens in a lot of cases: when extending/composing components, or when using Redux and the connect() wrapper. For example, the following component is not recognised:

class Theme extends Component {
  // ...
  render() {
    // ...
  }
}

function mapStateToProps(state) {
  const variables = selectVariables(state.theme.name)
  return {
    variables: selectVariables(state.theme.name),
    styles: selectStyles(state.theme.name, variables)
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(ThemeActions, dispatch)
}

export default connect(mapStateToProps, mapDispatchToProps)(Theme)

When using wrappers, we could use two files: _Theme.jsx (export "raw" component and use it for styleguide), and Theme (that uses wrappers etc.). However it would be great if it just work.

Any ideas on what needs to be done?

bug help wanted

Most helpful comment

@jameslaneconkling This looks like a bug in react-docgen.

All 27 comments

Any ideas on what needs to be done?

That鈥檚 what I wanted to ask you :-| No ideas right now.

Maybe the update of react-docgen will fix this.

If not, I believe this bug should be reported to react-docgen, as it is thrown from https://github.com/reactjs/react-docgen/blob/c94b7689484be5fb5b573d24d4d7c04833d03fd8/src/parse.js

I have worked a bit on this, and react-docgen will search for a "suitable" component definition in any of the exports. So,

Before:

// this throws the above error
module.exports = someWrappedComponent

After:

// react-docgen will find a component in module.exports.otherKey and use that.
module.exports = {
  default: someWrappedComponent,
  otherKey: suitableComponent
}

However when doing the latter react-styleguidist fails since it expects that a single default component is exported, see https://github.com/sapegin/react-styleguidist/blob/master/loaders/styleguide.loader.js#L26.

I think react-styleguidist should use whatever component react-docgen resolves to.

react-docgen do not export any information about the component object. I think we can add a custom resolver function to the config. Like this:

resolveComponent = function(filepath) {
    return 'require(' + JSON.stringify(filepath) + ').otherKey';
}

Any better ideas?

However it exports the resolver. I think something similar to this could work:

import recast from 'recast'
import { parse, resolve } from 'react-docgen'

export default function getComponentForStyleGuide(src) {
  var ast = recast.parse(src, {esprima: babylon});
  return resolver(ast.program, recast);
}

Yeah, that might work.

Any work-arounds for this until version 2 comes out?

@stinoga I think ignoring such components is the only way now.

@sapegin Does Radium cause this error as well? I've got the same error as above with a simple button component:

import React, {PropTypes} from 'react';
import classnames from 'classnames';
import Radium from 'radium';
import color from 'color';

import styles from './button.css';

class Button extends React.Component {
  render() {
    const {children, className, backgroundColor = '#0093D6', foregroundColor = '#FFFFFF', ...props} = this.props;
    let dynamicStyle = {
      backgroundColor : color(backgroundColor).hexString(),
      color : color(foregroundColor).hexString(),
      ':hover': {
        backgroundColor : color(backgroundColor).lighten(0.05).hexString(),
        boxShadow  : '0px 3px 6px 0px rgba(0,0,0,0.23), 0px 3px 6px 0px rgba(0,0,0,0.16)'
      },
      ':active': {
        backgroundColor : color(backgroundColor).darken(0.05).hexString(),
        borderBottomColor : color(backgroundColor).darken(0.05).hexString(),
        boxShadow: 'inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)'
      }
    };
    return (
      <button {...props} className={classnames(styles.button, className)} style={dynamicStyle}>
        {children}
      </button>
    );
  }
}

Button.propTypes = {
  /**
   * Set a background color on the button.
   */
  backgroundColor: PropTypes.string
};

export default Radium(Button);

@stinoga - I think I'm having the same problem. None of my Radium wrapped components are detected as suitable components.

@TigerC10 If you or anyone could submit a pull request to fix that it would be very cool.

I just ran into this as well on a very simple stateless functional component. Most of my components are written this way and this is the only one that errors:

import {PropTypes, createElement} from 'react';
import classNames from 'classnames';

const List = ({
    className,
    type,
    inline,
    children
}) => {
    const classes = classNames(
        'List',
        {'List-inline': inline},
        {[`List-${type}`]: type},
        className
    );
    const tag = (type === 'ordered') ? 'ol' : 'ul';

    return createElement(
        tag,
        {
            className: classes
        },
        children
    );
};

List.propTypes = {
    className: PropTypes.string,
    type: PropTypes.oneOf(['default', 'unstyled', 'ordered']),
    inline: PropTypes.bool,
    /** Children should only be made up of the ListItem component. */
    children: PropTypes.oneOfType([
        PropTypes.element,
        PropTypes.arrayOf(PropTypes.element)
    ]).isRequired
};

List.defaultProps = {
    className: '',
    type: 'unstyled'
};

export default List;

Any idea why this one in particular would error? I can't figure it out, the component works exactly as expected.

Nevermind, I figured it out. It's because React isn't in the component and I'm using createElement instead. React doesn't need to be there because the component doesn't really use it. Any idea on how to fix this or do I need to refactor my component and let it be there?

@reintroducing if you import 'react' and then use React.createElement does that help?

Another thing you can try is import 'react' and then use JSX to return like so:

const Tag = ...
return <Tag className={classes}>{children}</Tag>

@tizmagik I ended up refactoring the component altogether and haven't gotten back to this, unfortunately. Appreciate the heads up though.

Please test in 4.0.0, it might be fixed.

Closing this for now. Feel free to reopen if you still have this issue.

Hello,
I am getting the same error when using syled-components library or exporting my component using a similar methodology.
Example:

import React, { Component } from 'react';
import styled from 'styled-components';

class Button extends Component {
  render() {
    return <button>mybtn</button>;
  }
}
export default styled(Button)`
  border-color: 'red';
`;

I know this is probably a react-docgen issue... has anyone found a workaround?
Thanks guys.

@albermav I can reproduce it but have no idea why it doesn鈥檛 work. react-docgen can鈥檛 detect the right component for some reason.

/cc @mxstbr Could you please help with this us?

Hmm, that's weird, styled(Button) returns a React component? This must be an issue on the react-docgen side of things, maybe?


By the way, we don't recommend using styled-components like that. This is a nicer way of doing the same thing, on top of which it works with react-docgen I'm pretty sure:

import React, { Component } from 'react';
import styled from 'styled-components';

const Btn = styled.button`
  border-color: 'red';
`;

class Button extends Component {
  render() {
    return <Btn>mybtn</Btn>;
  }
}

export default Button;

@mxstbr Awesome, it works! I鈥檝e added an example to the FAQ so people could google it. Thank you very much!

Documenting this here in case it's of help to anyone.

Returning a ternary from a stateless component threw the No suitable component definition found Error.

const Tooltip = ({ x, y, xOffset, yOffset, children }) => (
  typeof x === 'number' && typeof y === 'number' ? (
    <div
      style={{
        position: 'absolute',
        top: y + yOffset,
        left: x + xOffset,
        pointerEvents: 'none'
      }}
    >
      { children }
    </div>
  ) : (
    <noscript />
  )
);

Refactoring to use an if statement fixed it:

const Tooltip = ({ x, y, xOffset, yOffset, children }) => {
  if (typeof x === 'number' && typeof y === 'number') {
    return (
      <div
        style={{
          position: 'absolute',
          top: y + yOffset,
          left: x + xOffset,
          pointerEvents: 'none'
        }}
      >
        { children }
      </div>
    );
  }

  return <noscript />;
};

@jameslaneconkling This looks like a bug in react-docgen.

Sorry for bring this up again but it seems that the styled-components with any component still an issue. So following the official guide https://www.styled-components.com/docs/basics#styling-any-components I still can't get the component being seen by styleguidist.

cc @mxstbr

Here is my example component:

import React from 'react'
import styled from 'styled-components'

const Menu = ({children, className}) => (
  <ul className={className}>
    {children}
  </ul>
)

export default styled(Menu)`
  padding: 1rem 0;
`

@mxstbr

By the way, we don't recommend using styled-components like that. This is a nicer way of doing the same thing, on top of which it works with react-docgen I'm pretty sure

Let me get this straight, so building a component like this is not recommended?

const RedDiv = styled.div`
  background-color: red;
`
export default RedDiv

it seems weird to me that I'd have to rewrite it to this:

const RedDiv = styled.div`
  background-color: red;
`
export default props => <RedDiv {...props} />

@MoeSattler no no no no that's 100% correct what you're doing there. It's just that the example given by @albermav had the class, so I kept it.

What we don't recommend:

class Button extends Component {
  render() {
    return <button className={this.props.className}>{this.props.children}</button>
  }
}

export default styled(Button)`
  color: blue;
`

Instead of doing that, do this:

const Button = styled.button`
  color: blue;
`

class BigButton extends Component {
  render() {
    return <Button>{this.props.children}</Button>
  }
}

export default BigButton;

It's even better when you can directly export the styled component, that's 100% perfect:

const Button = styled.button`
  color: blue;
`

export default Button;

That's what we want, I just assumed @albermav had a reason for the class there and this was just a trimmed-down example.

thanks for the clarification @mxstbr

Was this page helpful?
0 / 5 - 0 ratings