feat(react-native): add opt-in back handler API#310
Conversation
🦋 Changeset detectedLatest commit: c6d53cc The changes in this PR will be included in the next version bump. This PR includes changesets to release 28 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| @@ -0,0 +1,8 @@ | |||
| import { vi } from 'vitest'; | |||
|
|
|||
| export const GraniteBrownfieldModule = { | |||
There was a problem hiding this comment.
TODO: Brownfield test mock
| expect(parent).toHaveBeenCalledWith({ source: 'backButton' }); | ||
| }); | ||
|
|
||
| it('allows default navigation when handlers return false or undefined', () => { |
There was a problem hiding this comment.
default is false or undfined
| expect(parent).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('continues bubbling while handlers return false', () => { |
| const [hasBackHandler, setHasBackHandler] = useState(false); | ||
|
|
||
| const syncBackEventState = useCallback(() => { | ||
| setHasBackEvent(handlersRef.size > 0); | ||
| }, [handlersRef]); |
There was a problem hiding this comment.
If we change handlersRef.size > 0 to useMemo, hasBackEvent cannot be detected correctly because changes to ref.size do not trigger a render. We need to keep useCallback and explicitly update the state.
| resolve: { | ||
| alias: { | ||
| '@granite-js/brownfield-module': new URL('./test/brownfieldModuleMock.ts', import.meta.url).pathname, | ||
| 'react-native': new URL('./test/reactNativeMock.ts', import.meta.url).pathname, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
do not using resolve.
we are using @granite-js/vitest plugin
| const removeHandler = useCallback( | ||
| (...handlers: Array<BackHandlerCallback>) => { | ||
| for (const handler of handlers) { | ||
| handlersRef.delete(handler); | ||
| contextRemoveBackHandler(handler); | ||
| } | ||
| }, | ||
| [contextRemoveBackHandler, handlersRef] | ||
| ); | ||
|
|
||
| const addEventListener = useCallback( | ||
| (handler: BackHandlerCallback) => { | ||
| handlersRef.add(handler); | ||
| contextAddBackHandler(handler); | ||
|
|
||
| return { | ||
| remove: () => removeHandler(handler), | ||
| }; | ||
| }, | ||
| [contextAddBackHandler, handlersRef, removeHandler] | ||
| ); |
| initialProps: InitialProps; | ||
| initialScheme: string; | ||
| setIosSwipeGestureEnabled?: ({ isEnabled }: { isEnabled: boolean }) => Promise<void> | void; | ||
| setiOSBackPressHandler?: ({ handler }: { handler: () => void }) => Promise<void> | void; |
There was a problem hiding this comment.
how about oniOSBackPressHandler ?
Add
useBackHandleras a new subscription-based API for handling back actions before legacy useBackEvent handlers.useBackHandler runs handlers from the latest registration first and only prevents default back navigation when a handler explicitly returns true. Returning false or undefined allows bubbling to continue and eventually falls back to legacy
useBackEventhandlers or the router default back behavior.The existing useBackEvent API remains non-breaking: variadic add/remove behavior is preserved, all registered handlers still run, and handler return values are ignored.