-
-
Notifications
You must be signed in to change notification settings - Fork 157
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
using props.children results in an Out of Memory Error
#576
Comments
One kind of interesting detail, is that specifically with the web-export: when I load it with text, and while it is already loaded change it to props.children from text, it hot-reloads and actually works. But if I then refresh the browser and it metro regenerates the bundle, it crashes again like it does when you start it out with props.children. |
Out of Memory Error
Children should be ReactNode as far as i know Also for web i recommend this instead https://github.com/storybookjs/addon-react-native-web |
@dannyhw I'll try that addon and report back and see if it changes anything |
@the-simian let me know how if goes. Sorry I wasn't able to take a proper look into your example. If this continues to be an issue i would try removing this from the babel config and see if the issue goes away - ["babel-plugin-react-docgen-typescript", { exclude: "node_modules" }], This plugin does the auto args stuff but it can get stuck on certain types |
Ok I'll follow up on this, It was actually not using the plugin you mentioned beforehand. I added it and tried with it and without it and my experience was the same both ways here is my babel.config.js, and where I was toggling this particular plugin: module.exports = function (api) {
api.cache(true);
return {
env: {
production: {
plugins: ['transform-remove-console'],
},
},
presets: [
[
'babel-preset-expo',
{
jsxImportSource: 'nativewind', //I am using nativewind, but storybook does load successfully when not passing JSX Elements
},
],
'nativewind/babel',
],
// if this is here or not doesn't seem to matter.
// plugins: [
// ['babel-plugin-react-docgen-typescript', { exclude: 'node_modules' }],
// ],
};
}; So when I opened the issue I was loading the same storybook build on both the web and on the mobile device. This was react-native storybook itself. So before adding the second build via Based on your suggestion I switched the web build to using Just to simplify my example code: interface MyButtonProps {
onPress: () => void;
children?: ReactNode;
}
export const Button = ({ onPress, children }: MyButtonProps) => {
return (
<TouchableOpacity style={styles.container} onPress={onPress}>
<Text>{children}</Text>
</TouchableOpacity>
);
}; button.stories.tsx import type { Meta, StoryObj } from '@storybook/react';
import { Text, View } from 'react-native';
import { Button } from './button';
const meta = {
title: 'Button',
component: Button,
args: {},
decorators: [
(Story) => (
<View style={{ padding: 16 }}>
<Story />
</View>
),
],
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
//...works
export const BasicWorks: Story = {
args: {
children: 'Text Only',
},
};
//...fails
export const BasicFails: Story = {
args: {
children: <Text>Inside a JSX Element</Text>,
},
}; |
I wonder if this is some kind of interaction with the nativewind babel configuration. I've never seen this issue before and the fact that it works with hot reload makes me suspicious of some kind of bundling thing. |
@dannyhw I agree about the hot reload 'working' (but the instrumentation still believing its a string) leads me to also think its bundling related. I could make a repro with a 'minimum' build set up if you think that would help to diagnose. |
There is no proper storybook arg for children it will usually be an object type. You could try manually specifying the arg type for children. Yes a repro would be great if you can 🙏 |
Heres the example nativewind 4 repo that seems to work for me |
quick update: The repro repo you have works for me when I test the it out of the box
I copied and pasted this setup into another (larger) project, but then once again I got the same behavior. I'm trying to zero in on what could be different. Overall, the setup is the same, same metro config, babel config, dependency versions and so on. As before it runs with strings, but fails with nodes. The only that didn't work immediately was this error: storybookjs/storybook#15067 which was fixed by adding to the package.json:
The only thing that seems different offhand is just that we have more components, albeit none are referenced in the stories - there's just the one button story currently. Since your reproduction repo clearly works I can close this issue now, or I can follow through with troubleshooting and close it with a comment that addresses the root cause in case anyone else has the same issue. Either way I'll post anything I find here. Again thank you for doing so much to help me. Just seeing a working example is immensely useful. |
I've done some additional testing on this today. I was able to get some of logs by looking at the mobile build in chrome. I am still pretty perplexed about what's going on, but I can at least explain somewhat why the browser is running into an out of memory exception (and also so is the mobile device). Again, the 'simple' repro works, but in another project I'm getting this in the console:
This logs over and over until the browser crashes:
In order to read the trace I had to go into the sources tab of chrome and halt the debugger. Taking the advice of the linked documentation is a sort-of workaround, but doesn't work in every situation. const meta = {
title: 'MyButton',
component: MyButton,
args: {},
argTypes: {
children: {
options: ['Normal', 'Spicy'],
mapping: {
Normal: <Text className="color-primary">Normal</Text>,
Spicy: <Text className="color-red-900">Spicy</Text>,
},
control: { type: 'radio' },
},
},
decorators: [
(Story) => (
<View style={{ padding: 16 }}>
<Story />
</View>
),
],
} satisfies Meta<typeof MyButton>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Basic: Story = {
args: {
children: 'Spicy',
},
}; Here is the code for this. I can use the another aside: its recommend in the docs to use 'select' as a workaround. I used
... so "radio" it is I suppose. As for the error I can see that we're caught in a loop:
its looping through: I was able to get something interesting I can read when I step into the function like this: I am still perplexed about this and I've yet to ascertain a true root cause, but I've been digging deeper today and want to at least share my findings thusfar. |
Just to help contextualize, I'll post a step-through this 'loop' I will say that all this code is in the underlying storybook dependency as well as its lodash calls, but it only happens in the 'mobile build' the TL:DR: inferType is in an endless loop, and there's a cyclic object with forever-children. it calls itself recursively here: I'll start at infer type:
This calls inferType, and you can see its a 'recursive call', on the incoming
7 (same as step 1). back into the storybook code... .....And this repeats until there's no more memory. (here's the warning hit on the next iteration) |
I was able to find an issue in the main repo that has a somewhat similar-looking reproduction case and stack trace: I don't yet understand why in one situation (minimal repro) the JSX.Element (ReactNode) was serializable, but in the other situation there's the endless loop. |
Since this is somewhat related I'll add that there's also an open issue in storybook related to JSX.Element support. storybookjs/storybook#11428 To be clear, I am totally fine with seeing JSON representing a ReactNode in a textbox, like we have the minimal repro, just trying to get things to stop either breaking or infinitely looping. I am currently experimenting with other ways to use the mapping that works reliably in both the web and on mobile devices. Select breaks and radio only works in the browser for me thusfar. |
I can explain a little bit more about what's going on. Storybook for web is divided in two parts: the "preview", which renders your stories in an iframe, and the "manager" which renders the UI that surrounds that iframe. The two sides communicate via a "channel." So, for example, when you adjust a control in the manager, it sends a message back to the preview to render with the new value. The reason JSX screws things up is because it cannot naively be serialized across the channel as JSON. And the reason "mapping" works is because it's just sending the mapped value. In React Native, there is no iframe separation. But there's still a channel since it shares most of the same code with the web version. Hope that helps. At some point I would like to support JSX args properly per storybookjs/storybook#11428, but in the meantime mapping is your best bet. |
I also get this error when building a tamagui starter repo with expo react native and storybook react native |
@ditorahard did you try using a mapping like mentioned above? |
Describe the bug
Implementing props.children, or any situation where you pass a react component from a parent to a child results in an infinite load in the browser and also the native application
To Reproduce
https://github.com/dannyhw/expo-template-storybook
Code snippets
main.ts
/stories/button.tsx
/stories/button.stories.tsx
Expected behavior
Using
children
in args and passing a JSX.Element (such as<Text />
) should not freeze the browser in web export and nor on a device. It work the same as passing a string into the button in the above exampleScreenshots
These screenshots are in the browser, but the same behavior is also exhibited on a device; the splash screen stays up and the app never finishes loading after bundle download hits 100%
Using text:
eg:
this works.
Using props.children (like the above code snippet)
this never loads
System:
Additional Context
The text was updated successfully, but these errors were encountered: