Skip to content

Pro: View and Text remount their subtree when prop values change #667

Description

@Elehiggle

What happened?

Uniwind Pro's View wrapper picks its rendered component from the current props on every render. In this reproduction, a defined accessibilityState renders RNView, while undefined renders AccessibleView, which is unstable_NativeView on React Native 0.86.

When the prop changes between those states, React sees a different element type at the same position and replaces the view. The key and position stay the same, but child effects clean up and run again, local state is lost, and native children are recreated.

We hit this with an expo-image inside the view. When image preparation finished and accessibilityState changed to undefined, the tile remounted, expo-image emitted onLoadStart again, and the image was blank for one frame. Plain React Native View updates in place.

Text has the same root issue. Its choice between AccessibleText and RNText depends on current values such as numberOfLines, disabled, accessibility and press props, and the shape of children.

Expected: changing these props updates the existing component and preserves its subtree.

Actual: Uniwind changes the rendered component type and remounts the subtree.

The rendered element type should stay stable across updates while preserving React Native's prop and accessibility handling.

Reproduced on uniwind-pro 1.6.0. I also checked the shipped 1.7.0 package, and the same prop-dependent branches are still present in View.tsx and Text.tsx.

Steps to Reproduce

import { useEffect, useState } from 'react'
import { Text, View } from 'react-native'

function Probe() {
    useEffect(() => {
        console.log('[probe] mount')
        return () => console.log('[probe] unmount')
    }, [])

    return null
}

export default function Repro() {
    const [busy, setBusy] = useState(true)

    useEffect(() => {
        const timeout = setTimeout(() => setBusy(false), 1000)
        return () => clearTimeout(timeout)
    }, [])

    return (
        <View accessibilityState={busy ? { busy: true } : undefined}>
            <Probe />
            <Text>{busy ? 'busy' : 'idle'}</Text>
        </View>
    )
}
  1. Run this through the normal Uniwind Pro Metro resolver.
  2. Observe [probe] mount after the first render.
  3. After one second, observe [probe] unmount followed by another [probe] mount.
  4. Expected: the probe mounts once because only a prop value changed.
  5. As a control, use accessibilityState={{ busy }}. The extra unmount and mount no longer occur.
  6. Plain React Native without the Uniwind resolver also mounts the probe only once.

Snack or Repository Link (Optional)

No response

Uniwind version

uniwind-pro 1.6.0, installed as "uniwind": "npm:uniwind-pro@^1.6.0". Same wrapper code checked in 1.7.0.

React Native Version

0.86.3

Platforms

iOS

Expo

Yes

Additional information 〰

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions