ryer.io

The Flicker and the Pull-to-Refresh: Two Upgrade Regressions

TL;DR

  • Dropped items flicker back to their original position before settling at the destination.
  • Activation distance, component isolation, and stripping styles all failed to shift it.
  • Pull-to-refresh broke too: functional but visually stuck on iOS, native view errors on Android.
  • Moving the logic into the refreshControl prop fixed iOS; Android still shows a blank screen.
  • A syntax error was being masked by caching, which cost me real time.

More fallout from the React Native upgrade, in two flavours.

The flicker

Drag an item, release it, and it briefly appears back at its original position before settling where it belongs. Small, ugly, and completely reproducible.

My hypotheses were state handling or responder coordination between nested components. I worked through them:

Activation distance. Adjusting it to trigger dragging more smoothly did nothing. Worth trying since it’s cheap, but it was always a workaround rather than an explanation.

Component isolation. Manipulating parent and child interactions while logging state updates, looking for extraneous renders or mismanaged state. Nothing.

Stripping it bare. Removed styling, commented out effects, reduced components to bare functionality on the theory that some unnecessary render was causing it. The flicker persisted through all of it.

When a bug survives removal of everything you can remove, the cause is in what’s left. Here that’s the nestable scroll container and how it manages touch events.

I moved the container within the daily plan tab. Still flickering.

Then I found something that had been distorting the whole session: a syntax error that wasn’t failing, because caching was serving stale code. So some of my experiments hadn’t actually run. Debugging against a stale bundle means the results were meaningless, and I have no clean way to know which conclusions to keep.

I also found pull to refresh and nestable scroll container interlocking — commenting one out changed the other’s behaviour, which suggests they compete for the same touch handling.

GitHub discussions showed others hitting the same thing, with a common suggestion to downgrade react-native-draggable-flatlist. Rather than downgrade, I’m testing a different library advertised as a lightweight drop-in replacement. Downgrading means carrying a pinned old version indefinitely; if a replacement works, that’s a better place to end up.

Pull-to-refresh

Broken differently on each platform, which is usually informative.

  • iOS: functionally worked, but the spinner stayed down visually and never retracted.
  • Android: an error about native views.

I tried re-implementing with class-based components, which didn’t fix Android.

For iOS the fix was moving the logic into the refreshControl prop rather than handling it ourselves, then clamping UI updates with internal state and timeouts so the refreshing status is managed properly. The spinner behaves.

Android still shows a blank screen, and I suspect an interaction with third-party libraries rather than our code — which fits the pattern of the flicker, where two libraries both want the touch handling.

Next is the nested components and their dependencies, with Android’s third-party integration as the prime suspect. Round two.