I'm struggling with Ionic's lifecycle events used with RouterLink.
I have my app data in a JSON file that renders each item using the url paramenters when the page is loaded. I need buttons to go to the next or previous item and refresh the page content, ie:
/lecture/:param
) and update the page data.
The problem i'm facing is that RouterLink isn't triggering any Page Leaving lifecycle, so my states doesn't change, also, i can't clean the states (i can do it through an href param, but the whole app is refreshed).
I found similar threads on this, but some of them are outdated/still opened:
Routerlink bug github.Is there any way/workaround to reload the component and update the states passing different parameters?
I already tried using useHistory hook, history.replace (Source), and routerDirection="back" but none of them are triggering ion page leaving hooks. I also tried replacing IonRouterOutlet with Switch but this means not having some important ionic features.
Thank you in advance!!
Here is an extract of code for reference:
lectures.json (Array of objects)
{
"list" : [ {} ]
}
App.tsx
<IonApp>
<IonReactRouter>
<IonSplitPane contentId="main">
<Menu />
<IonRouterOutlet id="main">
<Route path="/" exact>
<Redirect to="/lectures" />
</Route>
<Route path="/lectures" exact component={Home}></Route>
<Route path="/lectures/:name" exact>
<Lecture />
</Route>
</IonRouterOutlet>
</IonSplitPane>
</IonReactRouter>
</IonApp>
Lecture.tsx
const Page: React.FC = () => {
const [lecturaIndex, setLecturaIndex] = useState<number>(0);
const [btnLinks, setBtnLinks] = useState<{next?: string , prev?: string }>({});
useIonViewDidEnter(()=>{
// Gets page params and use them to find current item in data json
let current;
current = lectures.list.find(lecture => lecture.title === match.params.name);
setLecturaIndex(current.id);
// Set next and prev buttons url for routerLink
setBtnLinks({
next: current.id < 13 ? `/lectures/${languages.list[current.id+1].title}` : '/lectures',
prev: current.id > 0 ? `/lectures/${languages.list[current.id-1].title}` : '/lectures'
});
// function that Enables/disables navigation buttons depending on current index. ie: if i=0; can't go back
indexCheck(current);
})
// THESE ARE NEVER TRIGGERED WHEN GOING TO NEXT PARAM (/lectures/:param)
useIonViewWillLeave(()=>{
console.log('will leave lecture');
})
useIonViewDidLeave(()=>{
console.log('did leave lecture');
})
return(
<IonPage>
<IonContent>
<IonButton
disabled={nextBtnDisable}
routerLink={btnLinks.next}
routerDirection="back"
>
NEXT
</IonButton>
</IonContent>
</IonPage>
)
}
Normally a useEffect
with dependency on the route match param is sufficient for issuing side-effects like refetching relevant data.
The ionic useIonViewWillEnter
and useIonViewDidEnter
hooks described here are basically mounting lifecycle hooks, and don't seem like they'd trigger if you're already on a view and only changing URL path parameters. Move the logic to a useEffect
hook which is called when component mounts and when any of its specified dependencies update.
Example:
useEffect(() => {
// business logic to handle name param changes
}, [match.params.name]);
If the match params is ever undefined then you can use Optional Chaining operator to guard against null/undefined object accesses.
useEffect(() => {
// business logic to handle name param changes
}, [match?.params?.name]);
It seems the useIonViewWillEnter
and useIonViewDidEnter
hooks do also take dependency arrays and internally implement a useEffect
hook. (should have dug a bit more previously)
export const useIonViewWillEnter = (callback: LifeCycleCallback, deps: any[] = []) => { const context = useContext(IonLifeCycleContext); const id = useRef<number | undefined>(); id.current = id.current || Math.floor(Math.random() * 1000000); useEffect(() => { callback.id = id.current!; context.onIonViewWillEnter(callback); }, deps); }; export const useIonViewDidEnter = (callback: LifeCycleCallback, deps: any[] = []) => { const context = useContext(IonLifeCycleContext); const id = useRef<number | undefined>(); id.current = id.current || Math.floor(Math.random() * 1000000); useEffect(() => { callback.id = id.current!; context.onIonViewDidEnter(callback); }, deps); };
The useEffect
hook isn't necessary then.
useIonViewDidEnter(() => {
// business logic to handle name param changes
}, [match?.params?.name]);