I’m using Storybook 5.2.6 for React 16.9.0 with Typescript 3.5.3 and using Material UI themed components.
Having added, and configured, #storybook/addon-docs the Storybook Docs page displays: “Cannot read property 'classes' of undefined” in the PropsTable when a component is wrapped withStyles from #material-ui.
Component:
import React, {FunctionComponent} from 'react';
import { Typography } from '#material-ui/core';
import {
withStyles,
WithStyles,
} from '#material-ui/core/styles';
import styles from './index.styles';
export interface IProps extends WithStyles<typeof styles> {
message?: string;
testId?: string;
}
const Bar: FunctionComponent<IProps> = (props) => {
const {
classes,
message,
testId = ‘test-bar',
} = props;
if (!message) { return null; }
return (
<Typography className={classes.message} data-testid={testId}>
{message}
</Typography>
);
};
export default withStyles(styles)(Bar);
Story
import React from 'react';
import { storiesOf } from '#storybook/react';
import { MuiThemeProvider } from '#material-ui/core/styles';
import Bar from './index';
import theme from '../../../others/global/material-ui-theme';
storiesOf('Bar', module)
.addDecorator(story => <MuiThemeProvider theme={theme}>{story()}</MuiThemeProvider>)
.addParameters({ component: Bar, componentSubtitle: 'Displays the Bar with message’ })
.add('Warning', () => <Bar message="warning" />);
In React devtools and debugging in Chrome devtools I can see the classes do get injected as props so I’m kinda stumped at the moment how to resolve this?
So a work around exists, you export the "unwrapped" component and use that as the component in docs:
As mentioned here:
https://github.com/storybookjs/storybook/issues/8361
and commented here: https://github.com/storybookjs/storybook/issues/8435#issuecomment-547075209
Example:
export const PureBar: FunctionComponent<IProps> = (props) => {
// ...
};
export default withStyles(styles)(PureBar);
Then in the story, update the component parameters to target the "Pure" component:
// import both the pure and wrapped components:
import Bar, { PureBar } from './Bar'
// Add to the story:
storiesOf('Bar', module)
.addParameters({ component: PureBar, componentSubtitle: 'Displays the Bar with message’ })
.add(/* ... */);
Related
I am following a guide to upgrade to React 18. After completing the upgrade I am seeing errors on certain pages in my app.
ReactDOM.unstable_renderSubtreeIntoContainer() is no longer supported in React 18.
I am not using the unstable_renderSubtreeIntoContainer() function anywhere in my app, but when I look closer at what is causing these errors it seems to be caused my Bootstrap components.
Is there anyway to update this to remove the errors?
I ran into the same problem with react-bootstrap#v0.33.1 specifically when using OverlayTrigger component after upgrading to React 18. The warning message suggests to migrate to using portals. So I implemented a CustomOverlayTrigger component that leverages portals and referred to React's portal documentation to do so. Note that this solution is for Bootstrap 3 usage of OverlayTrigger (react-bootstrap v0.33.1). It seems later versions of react-bootstrap got rid of using ReactDOM.unstable_renderSubtreeIntoContainer. If you are not in a position to migrate to later versions (like I am), this solution will help for this use case. I have not check thoroughly if other components use the deprecated method, but the approach might be the same.
First of all, I copied the original source of the OverlayTrigger component code located here. You will need to clean up the imports and include into your code the utils function createChainedFunction located here.
I then created a portal wrapper based off React's documentation that looks like this:
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
const tooltipRoot = document.getElementById('tooltip-root');
class PortalWrapper extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount() {
tooltipRoot.appendChild(this.el);
}
componentWillUnmount() {
tooltipRoot.removeChild(this.el);
}
render() {
// eslint-disable-next-line react/destructuring-assignment
return ReactDOM.createPortal(this.props.children, this.el);
}
}
PortalWrapper.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
};
export default PortalWrapper;
At the top, you can see the line const tooltipRoot = document.getElementById('tooltip-root');, I simply added in my index.html a div next to the react app's root div that will server as the anchor for my portal.
Then, back in the CustomOverlayTrigger component copied from react-bootstrap, I edited it in the follwing manner:
Remove all references to this._overlay and this._mountNode because the PortalWrapper now manages the mounting/unmounting. So I deleted componentDidMount(), componentDidUpdate(), componentWillUnmount() and renderOverlay()
I modified makeOverlay so that its result is wrapped by PortalWrapper so it became the following:
makeOverlay(overlay, props) {
return (
<PortalWrapper>
<Overlay
{...props}
show={this.state.show}
onHide={this.handleHide}
target={this}
>
{overlay}
</Overlay>
</PortalWrapper>
);
}
Finally, I changed the render method's return statement to become:
return (<>
{cloneElement(child, triggerProps)}
{this.makeOverlay(overlay, props)}
</>);
After this, I simply had to replace all my invocations to OverlayTrigger with CustomOverlayTrigger and I had the same result without the warning message.
I've installed the React Developer Tools extension on Google Chrome to debug a React application written in TypeScript, but once I start debugging the application and open the "Components" window, all components are shown as "Anonymous".
Granted, the application uses mostly function components.
Does anyone know if there is a way to get React Developer Tools to show component names in its component tree?
This happens when you define your components like so:
// Default arrow function export
export default () => {
// ...
}
// Default function export
export default function() {
// ...
}
You can replace with the following to fix the issue:
const CustomComponent = () => {
// ...
}
export default CustomComponent;
// or
export default function YourComponent() {
// ...
}
If you're using an exported anonymous functional component or a memo'ed component; it would be shown as Anonymous
Refer this - https://github.com/facebook/react/issues/17876
Or try solutions mentioned here - React Dev tools show my Component as Unknown
I'm trying to use Atomize (https://atomizecode.com/) with Gatsby JS (https://www.gatsbyjs.org/) and while it is successfully installed, the atomize components are not being rendered to the page.
I am trying to import a button to start "Hey", and while it renders something it is not the standard button component. Also, even though the button component is imported, I get the warning that reads it is not being used.
I am wrapping atomize around gatsby app by the following way:
import React from "react"
import { StyleReset } from "atomize"
export const wrapRootElement = ({ element }) => (
<>
<StyleReset />
{element}
</>
)
Did you install styletron toolkit?
According to their docs it's a dependency and looking at their package.json it's defined under peerDependencies which means it won't get installed along with atomize.
Styletron's docs sugggets using gatsby-plugin-styletron for Gatsby.
Hope that helps.
I've just updated a from a class based to a functional component.
When I look in React's DevTools, I'd usually see my component named Gallery with all the named state variables.
Now though, All I see is a component named _default with a bunch of non-descriptive State: definitions.
From other answers, I've read that React Dev Tools now supports hooks but I've not seen any examples of the component name being wrong.
Is this normal behaviour or is there something I'm doing wrong?
Versions
React 16.9.0
React Developer Tools Chrome extension: 4.1.1
Also getting the same issue in Firefox.
Component code
// The component
import React, { useState, useEffect } from 'react';
const Gallery = ({ images, layout }) => {
const [showLightbox, toggleLightbox] = useState(false);
const [activeImage, setActiveImage] = useState(false);
return (
// Some JSX here
)
};
Render code
// Rendering the component
import React from 'react';
import { render } from 'react-dom';
import Gallery from '../../global/scripts/components/Gallery';
render(
<Gallery images={images} />,
document.getElementById('image-gallery'),
);
Devtools screenshot
Try adding a displayName to your component before export. Check the following link for reference.
DisplayName
Use it like Gallery.displayName = 'Gallery'
I've got a (private) npm module that exports several React components. The module is bundled by Webpack and in the generated bundle a reference to one of the components (say Warning) looks like this:
t.d(n,"Warning",function(){return ge})
Then I've got a React project importing this module:
import { Warning } from 'my-custom-module';
...
render() {
return (
<Warning>Lorem ipsum</Warning>
);
}
This all works OK, but when I create a Jest snapshot of the component above, I expect the snapshot to look like
<Warning>Lorem ipsum</Warning>
but it looks like:
<ge>Lorem ipsum</ge>
For some reason Jest takes the minified identifier instead of the exported name of the component. How can I see the component name in the Jest snapshot? I'm unsure if I do need to adjust my Webpack config or the Jest setup...
Since you are referring the uglified version of the 'my-custom-module' it will try to render to the uglified names. However, I assume what you actually you need is to shallowly render your component.
You can use the Enzyme libraries's shallow renderer for this.
//MyAwesomeComponent.js
import { Warning } from 'my-custom-module';
export default class MyAwesomeComponent extends Component{
render(){
return (<Warning>Lorem ipsum</Warning>);
}
}
//MyAwesomeComponent.test.js
import { shallow } from 'enzyme';
import MyAwesomeComponent from './MyAwesomeComponent';
it('renders <MyAwesomeComponent />', () => {
const shallowMyComponent = shallow(<MyComponent />);
expect(shallowMyComponent).toMatchSnapshot()
});
This should show your snapshot as Warning without going a level deeper.