Description
🐛 Bug Report
RCTUIManager throws exceptions when managing children where a parent has both static (some without shadowViews) and dynamic content.
If the children contain any Defs component (which is/was returning nil for the shadowView in it's ViewManager) and the dynamic part changes size/number of elements, then the indices of the children do not match up.
Several similar issues have been opened in react-native-svg, and e.g. in several charting libraries built on top of it etc. and various workarounds exist, but the problem keeps popping up and should probably just work.
https://github.com/FormidableLabs/victory-native/issues/432
indiespirit/react-native-chart-kit#62
msand/react-native-svg-charts@e8b0baa
JesperLekland/react-native-svg-charts#280
JesperLekland/react-native-svg-charts#244
software-mansion/react-native-svg#258
software-mansion/react-native-svg#848
To Reproduce
git clone https://github.com/msand/ReproRCTUIManagerBug.git
cd ReproRCTUIManagerBug
yarn
react-native run-ios
Or do the following steps (using react-native-svg v7.0.0 - v.9.2.3, it's fixed in v9.2.4 and mostly didn't exist in v6.5.2 and earlier)
react-native init
yarn add react-native-svg@9.2.3 victory-native
react-native link
change App.js to reproduction code
react-native run-ios
Click either Increment or Decrement button
Expected Behavior
The chart renders without exceptions, and re-renders content according to the state.
Code Example
https://snack.expo.io/@msand/groaning-chocolate
https://github.com/msand/ReproRCTUIManagerBug
The code works in expo, but not with latest react-native.
Update: Doesn't work in Expo either, it just doesn't throw any exception, and does a partial re-render. If you click decrement twice, then the indices will align, and it'll start re-rendering with new content. At that point, incrementing will work as well.
Environment
React Native Environment Info:
System:
OS: macOS High Sierra 10.13.6
CPU: (4) x64 Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
Memory: 109.55 MB / 8.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 11.2.0 - /usr/local/bin/node
Yarn: 1.12.3 - /usr/local/bin/yarn
npm: 6.7.0 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 12.1, macOS 10.14, tvOS 12.1, watchOS 5.1
Android SDK:
API Levels: 16, 23, 24, 25, 26, 27, 28
Build Tools: 23.0.1, 25.0.0, 25.0.1, 25.0.2, 25.0.3, 26.0.1, 26.0.2, 26.0.3, 27.0.1, 27.0.2, 27.0.3, 28.0.2, 28.0.3
System Images: android-16 | Google APIs Intel x86 Atom, android-26 | Google APIs Intel x86 Atom_64, android-27 | Google APIs Intel x86 Atom, android-28 | Google APIs Intel x86 Atom
IDEs:
Android Studio: 3.3 AI-182.5107.16.33.5199772
Xcode: 10.1/10B61 - /usr/bin/xcodebuild
npmPackages:
react: 16.6.3 => 16.6.3
react-native: 0.58.4 => 0.58.4
npmGlobalPackages:
create-react-native-app: 2.0.2
react-native-cli: 2.0.1
react-native-create-library: 3.1.2
react-native-git-upgrade: 0.2.7
react-native-init: 0.6.7
Workaround
Change
return React.cloneElement(groupComponent, groupProps, [
clipComponent,
...React.Children.toArray(children),
]);
To
return React.cloneElement(groupComponent, groupProps, [
...React.Children.toArray(children),
clipComponent,
]);
This places the component which is missing the shadowView after the changing content. In some other cases, it requires wrapping the part with changing number of children in another parent, e.g. using a G element (used for grouping svg elements) to isolate it from the missing shadowView, i.e. Defs elements, causing the indices to misalign. Or, to isolate the Defs elements using a G element.
Exceptions
Exception when decrementing:
removedChildren count (0) was not what we expected (1)
Exception when incrementing
Exception '*** -[__NSArrayM insertObject:atIndex:]: index 4 beyond bounds [0 .. 2]' was thrown while invoking manageChildren on target UIManager with params (
55,
(
),
(
),
(
327
),
(
4
),
(
)
)
callstack: (
0 CoreFoundation 0x00000001129121bb __exceptionPreprocess + 331
1 libobjc.A.dylib 0x0000000110ff7735 objc_exception_throw + 48
2 CoreFoundation 0x000000011285e4ec _CFThrowFormattedException + 194
3 CoreFoundation 0x000000011283a794 -[__NSArrayM insertObject:atIndex:] + 1300
4 SvgExample 0x000000010f50357f -[RCTShadowView insertReactSubview:atIndex:] + 399
5 SvgExample 0x000000010f52d996 -[RCTUIManager _manageChildren:moveFromIndices:moveToIndices:addChildReactTags:addAtIndices:removeAtIndices:registry:] + 2966
6 SvgExample 0x000000010f52c8f6 -[RCTUIManager manageChildren:moveFromIndices:moveToIndices:addChildReactTags:addAtIndices:removeAtIndices:] + 342
7 CoreFoundation 0x000000011291903c __invoking___ + 140
...