Skip to main content

useIsFocused

Since useIsFocused() only applicable on Native, you can replace it with a custom function on Web.

Create your exports

hooks/use-is-focused.ts

ts
export { useIsFocused } from '@react-navigation/native'
ts
export { useIsFocused } from '@react-navigation/native'

hooks/use-is-focused.web.ts

ts
export function useIsFocused() {
return true
}
ts
export function useIsFocused() {
return true
}

On Web, screens are always focused when they're mounted.

Import your file

ts
import { useIsFocused } from 'hooks/use-is-focused'
ts
import { useIsFocused } from 'hooks/use-is-focused'

Custom Web Logic

This section is a little more advanced. It's relevant if you're using modals with Next Router.

In the above example, we always return true on Web.

One exception might be if there is a shallow routed modal on top of a screen.

For example, say that you have an EditArtist modal that shows when the URL contains a query param like /artists/drake?showsEditModal=true.

If you want to implement this logic, you could do it yourself on Web using a combination of React Context and checking query params with useRouter from next/router.

Create a context

First, in a file called context/modal-screen create your ModalScreenContext. This will let screens detect if they are a modal or not.

ts
import { createContext } from 'react'
 
export const ModalScreenContext = createContext(false)
ts
import { createContext } from 'react'
 
export const ModalScreenContext = createContext(false)

Then, your actual Next.js page could look something like this:

tsx
// pages/artist/[slug].tsx
import { ModalScreenContext } from 'context/modal-screen'
import { useRouter } from 'next/router'
export default function ArtistPage() {
const router = useRouter()
return (
<>
<ArtistScreen />
<ModalScreenContext.Provider value={true}>
{!!router?.query?.showsEditModal && <EditArtistModal />}
</ModalScreenContext.Provider>
</>
)
}
tsx
// pages/artist/[slug].tsx
import { ModalScreenContext } from 'context/modal-screen'
import { useRouter } from 'next/router'
export default function ArtistPage() {
const router = useRouter()
return (
<>
<ArtistScreen />
<ModalScreenContext.Provider value={true}>
{!!router?.query?.showsEditModal && <EditArtistModal />}
</ModalScreenContext.Provider>
</>
)
}

Notice that the EditArtistModal is wrapped with the ModalScreenContext.Provider, and the value is true.

This means that any component inside of EditArtistModal will know it is a modal, whereas every component inside of ArtistScreen will know it is not a modal.

Use the context

If you stick to the pattern of always including the word modal in the URL, you can use the useRouter hook to check if the URL contains the query param:

The logic for useIsFocused should now be this: your hook is focused if one of these conditions is true:

  1. the hook is inside of a (mounted) modal, or

  2. the hook is not a modal, and there is no modal mounted

    a. Why? If this hook is not in a modal, but a modal is mounted, then that means that this hook is underneath the modal, and thus not focused.

ts
import { useContext } from 'react'
import { useRouter } from 'next/router'
import { ModalScreenContext } from './context/modal-screen'
 
export function useIsFocused() {
const { query } = useRouter()
const iAmAModal = useContext(ModalScreenContext)
 
if (iAmAModal) {
return true
}
 
const isThereAModalVisibleOnTopOfMe =
// check if there is a query param with "modal"
Object.keys(query ?? {}).some((key) => key.toLowerCase().includes('modal'))
 
return !isThereAModalVisibleOnTopOfMe
}
ts
import { useContext } from 'react'
import { useRouter } from 'next/router'
import { ModalScreenContext } from './context/modal-screen'
 
export function useIsFocused() {
const { query } = useRouter()
const iAmAModal = useContext(ModalScreenContext)
 
if (iAmAModal) {
return true
}
 
const isThereAModalVisibleOnTopOfMe =
// check if there is a query param with "modal"
Object.keys(query ?? {}).some((key) => key.toLowerCase().includes('modal'))
 
return !isThereAModalVisibleOnTopOfMe
}

To use this as a repeatable method, it's important that you always use the word modal as a query param to denote that a modal is open.

Finally, you can navigate to this modal screen like so:

tsx
<Link href="/artists/drake" query={{ showsEditModal: true }} />
tsx
<Link href="/artists/drake" query={{ showsEditModal: true }} />

And if you implement some custom modal logic, you might end up having a link with a fancier as prop:

tsx
<Link href="/artists/drake" as="/@drake/edit" query={{ showsEditModal: true }} shallow />
tsx
<Link href="/artists/drake" as="/@drake/edit" query={{ showsEditModal: true }} shallow />

That way, you could put the edit modal as a full page when someone refreshes. But that's for another day...