Commit 53bf98b
Ignore stale image callbacks after an Image view is recycled on iOS (#58669)
Summary:
Fixes #58667.
On iOS with the new architecture, an `<Image>` can show the image of another `<Image>` that was unmounted just before. `RCTImageResponseObserverProxy` dispatches its callbacks to the main queue. `RCTImageComponentView` used one proxy for its whole lifetime, and `didReceiveImage` only checked that the view still had a state. When a view was recycled and handed to a new `<Image>` while a callback for the old request was still queued, that callback was applied to the new image. If the new source was cached it had already been applied synchronously on subscribe, so the late old image stayed on screen.
This change:
- creates a new `RCTImageResponseObserverProxy` for every subscription in `_setStateAndResubscribeImageResponseObserver`
- ignores image, progress and failure callbacks whose `observer` is not the current proxy (the `fromObserver:` argument was already passed but not used). For failures this also stops a stale error from clearing the new image.
The proxy's address identifies the subscription. The new proxy is allocated before the previous one is released, so two consecutive subscriptions never share an address. Reuse of an old address would need two resubscriptions while a callback from the first one is still queued.
## Changelog:
[IOS] [FIXED] - Image no longer shows the image of a previous source after its native view is recycled
Pull Request resolved: #58669
Test Plan:
Reproducer: https://github.com/mattijsf/rn-image-recycled-view-stale-image (React Native 0.87.1, also as a Snack: https://snack.expo.dev/mattijsf/image-recycle-stale-load-ios?platform=ios). It mounts 64 uncached remote images (red), replaces them with a cached one (green) as soon as the first red one has loaded, and counts a wrong image when the new image's `onLoad` reports the old image's size. A run is 50 rounds.
Release build on the iOS simulator (iPhone 17 Pro Max, iOS 26.5), React Native built from source (`RCT_USE_PREBUILT_RNCORE=0`), one run of 50 rounds each. The diff applies unchanged to 0.87.1.
| | rounds with a wrong image | wrong images |
| --- | --- | --- |
| 0.87.1 | 27 of 50 | 120 |
| 0.87.1 with this change | 0 of 50 | 0 |
With the change the red images still show up briefly before each swap, as intended, but none of them end up on a green tile.
Reviewed By: christophpurrer
Differential Revision: D121619393
Pulled By: javache
fbshipit-source-id: 78c29450bb25083667515c95f8c704d3b7db5eef1 parent 86c2cf1 commit 53bf98b
1 file changed
Lines changed: 13 additions & 5 deletions
Lines changed: 13 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
42 | | - | |
43 | 41 | | |
44 | 42 | | |
45 | 43 | | |
| |||
124 | 122 | | |
125 | 123 | | |
126 | 124 | | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
127 | 130 | | |
128 | 131 | | |
129 | 132 | | |
| |||
140 | 143 | | |
141 | 144 | | |
142 | 145 | | |
143 | | - | |
144 | | - | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
145 | 149 | | |
146 | 150 | | |
147 | 151 | | |
| |||
187 | 191 | | |
188 | 192 | | |
189 | 193 | | |
190 | | - | |
| 194 | + | |
191 | 195 | | |
192 | 196 | | |
193 | 197 | | |
| |||
196 | 200 | | |
197 | 201 | | |
198 | 202 | | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
199 | 207 | | |
200 | 208 | | |
201 | 209 | | |
| |||
0 commit comments