Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: don't react to snap point changes if container height is 0 #855

Merged
merged 1 commit into from Sep 9, 2022

Conversation

simon-abbott
Copy link
Contributor

Motivation

In React Native 65 the 'OnSnapPointChange' reaction sometimes fires when a view is going out of sight. Specifically it fires after the animatedContainerHeight has been set to 0. This causes the computed snap points to all be 0 themselves, which, in turn, causes the new calculated position to also be 0. All of this is not really an issue except that if the view then comes back into view without being recreated (like if a user presses 'back' on a navigation stack) the position is preserved when rendering with the recomputed now-non-zero snap points, which causes the sheet to snap to the fully open position.

Here is an example series of events:

  1. The BottomSheet is created with defined snap points ["25%", "75%"] and index 0.
  2. The view loads with a height of 1000.
  3. The snap points are correctly calculated to be [750, 250]
  4. The sheet is rendered at height 750
  5. The view is dismissed by navigating to another screen
  6. The animatedContainerHeight is calculated to be 0
  7. The snap points are calculated to be [0, 0]
  8. The OnSnapPointChange reaction is fired, which tries to animate to the nearest snap point, which is at position 0. Since both snap points are 0 it picks the last one, so index becomes 1, and position is set to 0.
  9. The navigation stack is popped, and the original View is displayed again.
  10. The animatedContainerHeight is once again calculated to be 1000
  11. The snap points are again calculated to be [750, 250].
  12. The OnSnapPointChange reaction is fired, which again animates to the nearest snap point. Since step 8 set the position to 0, the nearest snap point is always the last one, which in this case has index 1 (height = 250), and renders as fully open.

This patch fixes this issue by only running the OnSnapPointChange reaction when animatedContainerHeight > 0. If the height is zero then the internal state of the sheet does not matter since it won't be rendered anyway. With this change the aforementioned issue is resolved. Specifically, step 8 is skipped which means that step 12 runs against the last valid position (750 in the example), and the sheet stays where it should.

@github-actions
Copy link

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@simon-abbott
Copy link
Contributor Author

Not resolved, please don't close.

@github-actions
Copy link

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@simon-abbott
Copy link
Contributor Author

bump

@gorhom
Copy link
Owner

gorhom commented Apr 23, 2022

hi @simon-abbott thanks for submitting this pr, could you provide a repo video records of this issue or a sample code for this issue.

i have used this library with navigations but never faced this issue 🤔

@gorhom gorhom added the v4 Written in Reanimated v2 label Apr 23, 2022
@simon-abbott
Copy link
Contributor Author

simon-abbott commented Apr 25, 2022

@gorhom Well, in trying to record the issue I was unable to reproduce it. Seems the issue has been fixed in some other update of some dependency. If it re-occurs I'll reopen this PR.

@simon-abbott
Copy link
Contributor Author

simon-abbott commented May 4, 2022

@gorhom Unfortunately the problem did resurface, but this time I have proof: https://github.com/simon-abbott/BottomSheetMRE, and I can confirm that this patch still fixes the problem.

This video was taken on an LG G7 ThinQ running Android 9.

XRecorder_04052022_120447.mp4

@github-actions
Copy link

github-actions bot commented Jun 4, 2022

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@simon-abbott
Copy link
Contributor Author

Not stale, unfortunately.

@github-actions
Copy link

github-actions bot commented Jul 8, 2022

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@simon-abbott
Copy link
Contributor Author

Not stale.

@github-actions
Copy link

github-actions bot commented Aug 8, 2022

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@simon-abbott
Copy link
Contributor Author

Not stale

@github-actions
Copy link

github-actions bot commented Sep 8, 2022

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 10 days.

@simon-abbott
Copy link
Contributor Author

not stale

@gorhom
Copy link
Owner

gorhom commented Sep 9, 2022

looking into this PR now

In React Native 65 the 'OnSnapPointChange' reaction sometimes fires when
a view is going out of sight. Specifically it fires *after* the
`animatedContainerHeight` has been set to `0`. This causes the computed
snap points to all be `0` themselves, which, in turn, causes the `index`
calculation to select the last item in the snap point array. All of this
is not really an issue except that if the view then comes *back* into
view without being recreated (like if a user presses 'back' on a
navigation stack) the index is preserved when rendering the recomputed
now-non-zero snap points, so the sheet appears to snap to the fully open
position.

Here is an example series of events:

1. The BottomSheet is created with defined snap points `["25%", "75%"]`
   and index `0`.
2. The view loads with a height of `1000`.
3. The snap points are correctly calculated to be `[750, 250]`
4. The sheet is rendered at height `750`
5. The view is dismissed by navigating to another screen
6. The `animatedContainerHeight` is calculated to be `0`
7. The snap points are calculated to be `[0, 0]`
8. The `OnSnapPointChange` reaction is fired, which tries to animate to
   the nearest snap point, which is at position `0`. Since both snap
   points are `0` it picks the last one, so `index` becomes `1`, and
   position is set to `0`.
9. The navigation stack is popped, and the original View is displayed
   again.
10. The `animatedContainerHeight` is once again calculated to be `1000`
11. The snap points are again calculated to be `[750, 250]`.
12. The `OnSnapPointChange` reaction is fired, which again animates to
    the nearest snap point. Since step 8 set the position to `0`, the
    nearest snap point is always the last one, which in this case has
    index `1` (height = `250`), and renders as fully open.

This patch fixes this issue by only running the `OnSnapPointChange`
reaction when `animatedContainerHeight > 0`. If the height is zero then
the internal state of the sheet does not matter since it won't be
rendered anyway. With this change the aforementioned issue is resolved.
Specifically, step 8 is skipped which means that step 12 runs against
the last _valid_ position, and the sheet stays where it should.
@gorhom
Copy link
Owner

gorhom commented Sep 9, 2022

@simon-abbott thanks for submitting this PR, this issue is weird, you may need to cut an issue ticket to react navigation repo too

@gorhom gorhom merged commit 29af238 into gorhom:master Sep 9, 2022
@simon-abbott simon-abbott deleted the fix-snapping-open branch October 3, 2023 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
v4 Written in Reanimated v2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants