react-spectrum icon indicating copy to clipboard operation
react-spectrum copied to clipboard

useTabListState error if direct child is a React Fragment

Open mugen64 opened this issue 4 years ago โ€ข 5 comments

๐Ÿ› Bug Report

useTabListState fails with error Cannot convert a Symbol value to a string if tab items are wrapped in a react fragment

๐Ÿค” Expected Behavior

Should take fragment children as the tab items

๐Ÿ˜ฏ Current Behavior

Tries to parse react fragment as a tab item. Here is the stack trace

module.js:232 Uncaught TypeError: Cannot convert a Symbol value to a string
    at $f8429209754fda4b9142d514065f4$export$CollectionBuilder.getFullNode (module.js:232)
    at getFullNode.next (<anonymous>)
    at $f8429209754fda4b9142d514065f4$export$CollectionBuilder.iterateCollection (module.js:168)
    at iterateCollection.next (<anonymous>)
    at Object.[Symbol.iterator] (module.js:342)
    at Generator.next (<anonymous>)
    at new ListCollection (module.js:40)
    at factory (module.js:116)
    at module.js:378
    at mountMemo (react-dom.development.js:15846)
getFullNode @ module.js:232
iterateCollection @ module.js:168
[Symbol.iterator] @ module.js:342
ListCollection @ module.js:40
factory @ module.js:116
(anonymous) @ module.js:378
mountMemo @ react-dom.development.js:15846
useMemo @ react-dom.development.js:16219
useMemo @ react.development.js:1532
useCollection @ module.js:376
useListState @ module.js:121
useSingleSelectListState @ module.js:148
useTabListState @ module.js:10
(anonymous) @ index.js:16
renderWithHooks @ react-dom.development.js:14985
updateForwardRef @ react-dom.development.js:17044
beginWork @ react-dom.development.js:19098
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
beginWork$1 @ react-dom.development.js:23964
performUnitOfWork @ react-dom.development.js:22779
workLoopSync @ react-dom.development.js:22707
renderRootSync @ react-dom.development.js:22670
performSyncWorkOnRoot @ react-dom.development.js:22293
scheduleUpdateOnFiber @ react-dom.development.js:21881
updateContainer @ react-dom.development.js:25482
(anonymous) @ react-dom.development.js:26021
unbatchedUpdates @ react-dom.development.js:22431
legacyRenderSubtreeIntoContainer @ react-dom.development.js:26020
render @ react-dom.development.js:26103
(anonymous) @ render.js:40
render @ render.js:39
_callee$ @ render.js:131
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ render.js:9
_next @ render.js:11
(anonymous) @ render.js:11
(anonymous) @ render.js:11
_renderMain @ render.js:140
renderMain @ render.js:103
_callee3$ @ StoryRenderer.js:379
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ StoryRenderer.js:13
_next @ StoryRenderer.js:15
Promise.then (async)
asyncGeneratorStep @ StoryRenderer.js:13
_next @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
renderStory @ StoryRenderer.js:411
_callee2$ @ StoryRenderer.js:257
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ StoryRenderer.js:13
_next @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
renderStoryIfChanged @ StoryRenderer.js:281
_callee$ @ StoryRenderer.js:151
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ StoryRenderer.js:13
_next @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
renderCurrentStory @ StoryRenderer.js:165
(anonymous) @ StoryRenderer.js:78
(anonymous) @ index.js:168
handleEvent @ index.js:167
handler @ index.js:93
emit @ index.js:100
setSelection @ story_store.js:859
finishConfiguring @ story_store.js:393
ConfigApi.configure @ config_api.js:26
(anonymous) @ loadCsf.js:254
configure @ index.js:19
(anonymous) @ generated-stories-entry.js:6
./.storybook/generated-stories-entry.js @ generated-stories-entry.js:6
__webpack_require__ @ bootstrap:853
fn @ bootstrap:150
0 @ global.css?3d5c:82
__webpack_require__ @ bootstrap:853
checkDeferredModules @ bootstrap:45
webpackJsonpCallback @ bootstrap:32
(anonymous) @ main.iframe.bundle.js:1
Show 61 more frames
react-dom.development.js:20085 The above error occurred in the <FynTabs> component:

    at http://localhost:6006/main.iframe.bundle.js:17707:24
    at div
    at unboundStoryFn (http://localhost:6006/vendors~main.iframe.bundle.js:44589:30)
    at ErrorBoundary (http://localhost:6006/vendors~main.iframe.bundle.js:56678:5)

React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.
logCapturedError @ react-dom.development.js:20085
update.payload @ react-dom.development.js:20133
getStateFromUpdate @ react-dom.development.js:12102
processUpdateQueue @ react-dom.development.js:12250
resumeMountClassInstance @ react-dom.development.js:12921
updateClassComponent @ react-dom.development.js:17430
beginWork @ react-dom.development.js:19073
beginWork$1 @ react-dom.development.js:23940
performUnitOfWork @ react-dom.development.js:22779
workLoopSync @ react-dom.development.js:22707
renderRootSync @ react-dom.development.js:22670
performSyncWorkOnRoot @ react-dom.development.js:22293
scheduleUpdateOnFiber @ react-dom.development.js:21881
updateContainer @ react-dom.development.js:25482
(anonymous) @ react-dom.development.js:26021
unbatchedUpdates @ react-dom.development.js:22431
legacyRenderSubtreeIntoContainer @ react-dom.development.js:26020
render @ react-dom.development.js:26103
(anonymous) @ render.js:40
render @ render.js:39
_callee$ @ render.js:131
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ render.js:9
_next @ render.js:11
(anonymous) @ render.js:11
(anonymous) @ render.js:11
_renderMain @ render.js:140
renderMain @ render.js:103
_callee3$ @ StoryRenderer.js:379
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ StoryRenderer.js:13
_next @ StoryRenderer.js:15
Promise.then (async)
asyncGeneratorStep @ StoryRenderer.js:13
_next @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
renderStory @ StoryRenderer.js:411
_callee2$ @ StoryRenderer.js:257
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ StoryRenderer.js:13
_next @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
renderStoryIfChanged @ StoryRenderer.js:281
_callee$ @ StoryRenderer.js:151
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ StoryRenderer.js:13
_next @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
(anonymous) @ StoryRenderer.js:15
renderCurrentStory @ StoryRenderer.js:165
(anonymous) @ StoryRenderer.js:78
(anonymous) @ index.js:168
handleEvent @ index.js:167
handler @ index.js:93
emit @ index.js:100
setSelection @ story_store.js:859
finishConfiguring @ story_store.js:393
ConfigApi.configure @ config_api.js:26
(anonymous) @ loadCsf.js:254
configure @ index.js:19
(anonymous) @ generated-stories-entry.js:6
./.storybook/generated-stories-entry.js @ generated-stories-entry.js:6
__webpack_require__ @ bootstrap:853
fn @ bootstrap:150
0 @ global.css?3d5c:82
__webpack_require__ @ bootstrap:853
checkDeferredModules @ bootstrap:45
webpackJsonpCallback @ bootstrap:32
(anonymous) @ main.iframe.bundle.js:1
Show 48 more frames
index.js:54 TypeError: Cannot convert a Symbol value to a string
    at $f8429209754fda4b9142d514065f4$export$CollectionBuilder.getFullNode (module.js:232)
    at getFullNode.next (<anonymous>)
    at $f8429209754fda4b9142d514065f4$export$CollectionBuilder.iterateCollection (module.js:168)
    at iterateCollection.next (<anonymous>)
    at Object.[Symbol.iterator] (module.js:342)
    at Generator.next (<anonymous>)
    at new ListCollection (module.js:40)
    at factory (module.js:116)
    at module.js:378
    at mountMemo (react-dom.development.js:15846)

๐Ÿ’ Possible Solution

๐Ÿ”ฆ Context

Rendering different children based on a condition eg

<>
{
 casSeeThis && (<>...</>)
}
...
</>

or if you are providing the children prop as part of an object { children: <>...</>} This makes it a requirement to manually drill into the children prop and check for child type before passing them onto state. I understand the lib should not do everything for you. So if this issue will not be fixed. Documentation on how to get around it or to get around wrapping any of the expected direct children, would be a great help as the error given is not helpful.

๐Ÿ’ป Code Sample

๐ŸŒ Your Environment

Software Version(s)
@react-stately/tabs ^3.0.0
react ^17.0.2
react-dom ^17.0.2
@storybook/react ^6.2.8
Browser chrome
Operating System windows 10

๐Ÿงข Your Company/Team

๐Ÿ•ท Tracking Issue (optional)

mugen64 avatar Jun 27 '21 09:06 mugen64

The first sentence of this section should help https://react-spectrum.adobe.com/react-stately/collections.html#dynamic-collections Essentially you have a collection that needs to change, so you'll need to create a new 'items' object to pass in, with or without the tab you're trying to conditionally render. You'll notice, for example, that if you try to conditionally apply anything to static collections, it won't update the rendered output. We don't have access to React internals and therefore can't know if a child of the collection was updated. Any state that may cause a different render needs to be passed into items

snowystinger avatar Jun 27 '21 17:06 snowystinger

Hey! good news, talked with the team, we think this could be supported in https://github.com/adobe/react-spectrum/blob/main/packages/@react-stately/collections/src/CollectionBuilder.ts General gist would be that we know when we come across a React Fragment and so we can look at the children. We can't do this in other cases as we don't know the element types, but for React Fragment this should be ok.

snowystinger avatar Jun 30 '21 17:06 snowystinger

Hi, any updates on that? I have the same problem with list component

DenJel7 avatar Jan 29 '23 17:01 DenJel7

I believe this has been addressed in our new collections implementation https://codesandbox.io/p/sandbox/pedantic-lovelace-pqjllx?file=%2Fsrc%2FApp.js%3A25%2C10 We use them in our RAC components. We haven't documented it for just hooks yet, and we haven't implemented it in our react spectrum components yet.

Feel free to have a look at the source for the RAC components though.

snowystinger avatar Jan 02 '24 15:01 snowystinger

@snowystinger - would #6430 fix it in CollectionBuilder?

solimant avatar May 22 '24 04:05 solimant

@solimant I believe so. I don't have a codesandbox for the original issue, I'm going to close this now. If there's still an issue, please provide a codesandbox.

snowystinger avatar Aug 20 '24 07:08 snowystinger