new_reactjs
new_reactjs

Reputation: 228

How to clear the deep link after it was opened?

I have a react native app, on the Home Screen I have the code to open the deep link. I check if the deep link exists using Linking.getInitialURL() and then I redirect the user if the initialUrl exists, this flow works fine but the deep link doesn't get cleared.

For example,if I click on the deep link myapp://home/gallery it takes me to a Gallery screen. When I click on Go Home button on the Gallery screen, I go to the Home Screen. But Linking.getInitialURL() detects the myapp://home/gallery link agan and redirects the user back to the Gallery screen. Only after this, if I go to the Home screen the initialUtl is null. How can I clear the Linking.getInitialURL() after the link has been opened already the first time, why the react-native doesn't detect it?

I tried to check Linking.getInitialURL() every time screen focuses( by refreshing the screen) but still Linking.getInitialURL() is returning the deep link first time the user goes to home from gallery.

Any help and advise is appreciated.

Edit (added code)

// Home screen

useEffect(()=> {

const getLink = async () => {

const link = Linking.getInitialURL()

if (link){
await Linking.openURL(url)}
}

getLink()
},[])

Upvotes: 4

Views: 3955

Answers (6)

Rick Nijsse
Rick Nijsse

Reputation: 1

I've found a solution that works for our needs. The issue was that the url provided from the getInitialUrl (if the app was opened from a deeplink situation), matched the url provided from the eventListener. So the app did not see a change, because the url was the same.

What I did was to generate an unique deeplink url by adding a timestamp into it. my-app:///deeplink/:timestamp/path/to/my/page.

Then I can listen to it by using the Linking.useUrl() hook. The following example comes straight from the app as I needed the parsed data instead of the full url. But you can figure it out :)

export default function useUrl() {
  const url = Linking.useURL(); // Hook to listen for deep links
  const [parsedUrl, setParsedUrl] = useState<ParsedURL>();

  useEffect(() => {
    if (!url) {
      setParsedUrl(undefined);
      return;
    }

    setParsedUrl(Linking.parse(url));
  }, [url]);

  return { ...parsedUrl };
}

Might not be a solution that will work for everyone, as the url might not be unique every time. But at least it's a way. Hope it helps!

Upvotes: 0

Michael Czolko
Michael Czolko

Reputation: 2858

You have to handle the URL logic yourself.

Ideally check for initial URL and on every change handle URL accordingly.

const onChange = (event: { url: string }) => handleUrl(event.url)

const handleUrl = async (url: string) => {
  // your logic...
}

useEffect(() => {
  getInitialURL().then((url) => {
    url && handleUrl(url)
  })
  const subscription = addEventListener('url', onChange)
  return () => subscription.remove()
}, [])

This way you sure about every change of the URL.

Upvotes: 0

Thiago Fernando
Thiago Fernando

Reputation: 1

I had a similar issue recently and you can get that behaviour by using Linking.addEventListener('url', handleOpenURL);.

The event will be triggered only when the user clicks on the external link, and it will not be called when the App is "active".

https://docs.expo.dev/versions/latest/sdk/linking/#linkingaddeventlistenertype-handler

useEffect(() => {
   Linking.addEventListener('url', handleOpenURL);
},[])

const handleOpenURL = (_url) => {
  console.log(_url)
  // open the URL
}

Upvotes: 0

Smooth1884
Smooth1884

Reputation: 31

Storing the link in a useState didn't work for my use case as I need it to be possible that a user clicks on a link twice in a row.

So I found a way by replacing the URL using Linking.openURL() and changing the URL with that. after that not handling that new URL

Upvotes: 0

Max
Max

Reputation: 95

Perhaps in your case you can store the link in central state (like redux) and compare the link which is about to be opened with the one in central state.

Also, I think RN recommends checking a link with canOpenURL before opening it.

Something like this

const { centrallyStoredDeepLink } = someCentralStore;

// Home screen

useEffect(()=> {

const getLink = async () => {

const link = Linking.getInitialURL()

if (link && link !== centrallyStoredDeeplink && (await Linking.canOpenURL(link)){
centrallyStoredDeepLink = link; //You'll have to use your stores code for setting here
await Linking.openURL(url)}
}

getLink()
},[])

Upvotes: 0

Fapi
Fapi

Reputation: 356

you are using useEffect() and as you configured it it probably fires when you load the Home screen - please verify the same with some logs.

In general I would not specify the function within the useEffect block and use a const with useState() to store the state for link. Then you can put the same in the [linkState] brackets in useEffect, thus useEffect will only fire when the linkState has changed.

Additionally you are working here with deep links, then I would rather have the code for the same in the navigation index.js. That will allow you to avoid any side effects like you are facing.

Upvotes: 0

Related Questions