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
+5 -5
View File
@@ -21,12 +21,12 @@ export function useGoogleTranslate() {
if (IS_ANDROID) { if (IS_ANDROID) {
try { try {
// use `getApplicationIconAsync` to determine if the translate app is installed // use `getApplicationIconAsync` to determine if the translate app is installed
if ( const installed = await IntentLauncher.getApplicationIconAsync(
!(await IntentLauncher.getApplicationIconAsync(
'com.google.android.apps.translate', 'com.google.android.apps.translate',
)) )
) { if (!installed) {
throw new Error('Translate app not installed') openLink(translateUrl)
return
} }
// TODO: this should only be called one at a time, use something like // TODO: this should only be called one at a time, use something like
@@ -83,10 +83,10 @@ export function BetaFeaturesSettingsScreen({}: Props) {
} catch (e) { } catch (e) {
logger.error('Failed to toggle beta features', {safeMessage: e}) logger.error('Failed to toggle beta features', {safeMessage: e})
Toast.show(l`Something went wrong, please try again.`, {type: 'error'}) Toast.show(l`Something went wrong, please try again.`, {type: 'error'})
return
} finally {
setIsPending(false) setIsPending(false)
return
} }
setIsPending(false)
/* /*
* The toggle already succeeded; re-evaluate feature gates against the new * The toggle already succeeded; re-evaluate feature gates against the new
* attribute in-session as a best-effort follow-up. A failure here should * 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}) dispatch({type: 'setIsLoading', value: true})
const serviceDid = state.serviceDescription?.did ?? 'UNKNOWN'
try { try {
const {available: handleAvailable} = await checkHandleAvailability( const {available: handleAvailable} = await checkHandleAvailability(
createFullHandle(handle, state.userDomain), createFullHandle(handle, state.userDomain),
state.serviceDescription?.did ?? 'UNKNOWN', serviceDid,
{}, {},
) )
@@ -90,6 +91,7 @@ export function StepHandle() {
value: _(msg`That username is already taken`), value: _(msg`That username is already taken`),
field: 'handle', field: 'handle',
}) })
dispatch({type: 'setIsLoading', value: false})
return return
} else { } else {
ax.metric('signup:handleAvailable', {typeahead: false}) ax.metric('signup:handleAvailable', {typeahead: false})
@@ -99,9 +101,8 @@ export function StepHandle() {
safeMessage: error, safeMessage: error,
}) })
// do nothing on error, let them pass // do nothing on error, let them pass
} finally {
dispatch({type: 'setIsLoading', value: false})
} }
dispatch({type: 'setIsLoading', value: false})
ax.metric('signup:nextPressed', { ax.metric('signup:nextPressed', {
activeStep: state.activeStep, activeStep: state.activeStep,
+4 -3
View File
@@ -322,6 +322,7 @@ export function useSubmitSignup() {
dispatch({type: 'setError', value: ''}) dispatch({type: 'setError', value: ''})
dispatch({type: 'setIsLoading', value: true}) dispatch({type: 'setIsLoading', value: true})
const verificationCode = state.pendingSubmit?.verificationCode
try { try {
await createAccount( await createAccount(
{ {
@@ -331,7 +332,7 @@ export function useSubmitSignup() {
password: state.password, password: state.password,
birthDate: state.dateOfBirth, birthDate: state.dateOfBirth,
inviteCode: state.inviteCode.trim(), inviteCode: state.inviteCode.trim(),
verificationCode: state.pendingSubmit?.verificationCode, verificationCode,
}, },
{ {
signupDuration: Date.now() - state.signupStartTime, signupDuration: Date.now() - state.signupStartTime,
@@ -360,6 +361,7 @@ export function useSubmitSignup() {
field: 'invite-code', field: 'invite-code',
}) })
dispatch({type: 'setStep', value: SignupStep.INFO}) dispatch({type: 'setStep', value: SignupStep.INFO})
dispatch({type: 'setIsLoading', value: false})
return return
} }
@@ -384,9 +386,8 @@ export function useSubmitSignup() {
safeMessage: e, safeMessage: e,
}) })
} }
} finally {
dispatch({type: 'setIsLoading', value: false})
} }
dispatch({type: 'setIsLoading', value: false})
}, },
[l, ax, createAccount, onboardingDispatch], [l, ax, createAccount, onboardingDispatch],
) )
+18 -3
View File
@@ -10,6 +10,16 @@ import {updatePostShadow} from '../cache/post-shadow'
import {useAppviewClient, useSession} from '../session' import {useAppviewClient, useSession} from '../session'
import {useProfileUpdateMutation} from './profile' 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() { export function usePinnedPostMutation() {
const {_} = useLingui() const {_} = useLingui()
const {currentAccount} = useSession() const {currentAccount} = useSession()
@@ -27,20 +37,25 @@ export function usePinnedPostMutation() {
postCid: string postCid: string
action: 'pin' | 'unpin' action: 'pin' | 'unpin'
}) => { }) => {
if (!currentAccount) throw new Error('Not signed in')
const pinCurrentPost = action === 'pin' const pinCurrentPost = action === 'pin'
let prevPinnedPost: string | undefined let prevPinnedPost: string | undefined
try { try {
updatePostShadow(queryClient, postUri, {pinned: pinCurrentPost}) updatePostShadow(queryClient, postUri, {pinned: pinCurrentPost})
// get the currently pinned post so we can optimistically remove the pin from it // 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, { const profile = await client.call(app.bsky.actor.getProfile, {
actor: currentAccount.did, actor: currentAccount.did,
}) })
prevPinnedPost = profile.pinnedPost?.uri prevPinnedPost = getPinnedPostUri(profile)
if (prevPinnedPost && prevPinnedPost !== postUri) { 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}) updatePostShadow(queryClient, prevPinnedPost, {pinned: false})
} }
}
await profileUpdateMutate({ await profileUpdateMutate({
profile, profile,