Unblock React Compiler for 5 components with early exits inside try

Where a `return` sits in the try or the catch, the `finally` body has to be
duplicated on that exit path before it can be hoisted below the try/catch.
Where a `throw` is used to jump to the catch, an explicit test replaces it.

Each of these then revealed a second cause in the same try, so the fixes go
together: an optional chain hoisted above the try where it does not depend on
anything the try produces, moved into a module-scope helper where it does, and
one `&&` in an if-test split into nested ifs.

Skipped components: 125 -> 120.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tomek Zawadzki
2026-08-25 16:40:33 +02:00
parent f87fdd2ea2
commit 73a12c44b1
5 changed files with 35 additions and 18 deletions
+6 -6
View File
@@ -21,12 +21,12 @@ export function useGoogleTranslate() {
if (IS_ANDROID) {
try {
// use `getApplicationIconAsync` to determine if the translate app is installed
if (
!(await IntentLauncher.getApplicationIconAsync(
'com.google.android.apps.translate',
))
) {
throw new Error('Translate app not installed')
const installed = await IntentLauncher.getApplicationIconAsync(
'com.google.android.apps.translate',
)
if (!installed) {
openLink(translateUrl)
return
}
// TODO: this should only be called one at a time, use something like
@@ -83,10 +83,10 @@ export function BetaFeaturesSettingsScreen({}: Props) {
} catch (e) {
logger.error('Failed to toggle beta features', {safeMessage: e})
Toast.show(l`Something went wrong, please try again.`, {type: 'error'})
return
} finally {
setIsPending(false)
return
}
setIsPending(false)
/*
* The toggle already succeeded; re-evaluate feature gates against the new
* attribute in-session as a best-effort follow-up. A failure here should
+4 -3
View File
@@ -76,10 +76,11 @@ export function StepHandle() {
dispatch({type: 'setIsLoading', value: true})
const serviceDid = state.serviceDescription?.did ?? 'UNKNOWN'
try {
const {available: handleAvailable} = await checkHandleAvailability(
createFullHandle(handle, state.userDomain),
state.serviceDescription?.did ?? 'UNKNOWN',
serviceDid,
{},
)
@@ -90,6 +91,7 @@ export function StepHandle() {
value: _(msg`That username is already taken`),
field: 'handle',
})
dispatch({type: 'setIsLoading', value: false})
return
} else {
ax.metric('signup:handleAvailable', {typeahead: false})
@@ -99,9 +101,8 @@ export function StepHandle() {
safeMessage: error,
})
// do nothing on error, let them pass
} finally {
dispatch({type: 'setIsLoading', value: false})
}
dispatch({type: 'setIsLoading', value: false})
ax.metric('signup:nextPressed', {
activeStep: state.activeStep,
+4 -3
View File
@@ -322,6 +322,7 @@ export function useSubmitSignup() {
dispatch({type: 'setError', value: ''})
dispatch({type: 'setIsLoading', value: true})
const verificationCode = state.pendingSubmit?.verificationCode
try {
await createAccount(
{
@@ -331,7 +332,7 @@ export function useSubmitSignup() {
password: state.password,
birthDate: state.dateOfBirth,
inviteCode: state.inviteCode.trim(),
verificationCode: state.pendingSubmit?.verificationCode,
verificationCode,
},
{
signupDuration: Date.now() - state.signupStartTime,
@@ -360,6 +361,7 @@ export function useSubmitSignup() {
field: 'invite-code',
})
dispatch({type: 'setStep', value: SignupStep.INFO})
dispatch({type: 'setIsLoading', value: false})
return
}
@@ -384,9 +386,8 @@ export function useSubmitSignup() {
safeMessage: e,
})
}
} finally {
dispatch({type: 'setIsLoading', value: false})
}
dispatch({type: 'setIsLoading', value: false})
},
[l, ax, createAccount, onboardingDispatch],
)
+19 -4
View File
@@ -10,6 +10,16 @@ import {updatePostShadow} from '../cache/post-shadow'
import {useAppviewClient, useSession} from '../session'
import {useProfileUpdateMutation} from './profile'
/**
* Out of the hook because React Compiler cannot lower an optional chain inside a
* `try` block, and `profile` only exists once the request inside it resolves.
*/
function getPinnedPostUri(profile: {
pinnedPost?: {uri: string}
}): string | undefined {
return profile.pinnedPost?.uri
}
export function usePinnedPostMutation() {
const {_} = useLingui()
const {currentAccount} = useSession()
@@ -27,19 +37,24 @@ export function usePinnedPostMutation() {
postCid: string
action: 'pin' | 'unpin'
}) => {
if (!currentAccount) throw new Error('Not signed in')
const pinCurrentPost = action === 'pin'
let prevPinnedPost: string | undefined
try {
updatePostShadow(queryClient, postUri, {pinned: pinCurrentPost})
// get the currently pinned post so we can optimistically remove the pin from it
if (!currentAccount) throw new Error('Not signed in')
const profile = await client.call(app.bsky.actor.getProfile, {
actor: currentAccount.did,
})
prevPinnedPost = profile.pinnedPost?.uri
if (prevPinnedPost && prevPinnedPost !== postUri) {
updatePostShadow(queryClient, prevPinnedPost, {pinned: false})
prevPinnedPost = getPinnedPostUri(profile)
if (prevPinnedPost) {
// Nested rather than `&&`: React Compiler cannot lower a logical
// expression in a test position inside a `try`.
if (prevPinnedPost !== postUri) {
updatePostShadow(queryClient, prevPinnedPost, {pinned: false})
}
}
await profileUpdateMutate({