0

I have a quite traditional problem but I haven't found a solution thus far searching around on Stackoverflow.

My problem is like this. I have a functional component which renders a form (using Formik). This component is rendered inside another Tab component that allows user to switch to another view. The UI also have another menu, buttons which also allow user to switch to different UI. Whenever a user changes a field in the form, I have a global flag in Redux store that marks it as dirty form.

What I want to achieve is that when user click on another tab or go to another UI view when form is dirty, system will show a dialog to confirm. If user agrees, he loses his changes and go to another view, if he doesn't, he will stay at current view with the form.

My current solution is that I have to add the confirming logic to every event handler that could bring user to another view such as Tab click, Menu click and this is tedious. Not to mention if the UI change in the future with more events for example more buttons, we have to keep adding this confirm logic to the onClick handler.

I also tried using useEffect as below in this component that returns a clean up function where I can add the confirm logic. This could show up the confirm dialog but it won't prevent React to unmount this form component and user ends up still go to another view.

useEffect(() => {
  return () => {
    //confirm logic here
  }
}, [])

Is there a way for functional component handle this confirming logic itself?

These are some post I looked at but don't really find the answer. This one suggest using React router which we don't use in our project. The route actually doesn't change when user leave this form component. Show warning message before unmouting React component

Thanks for any advice!

Hoang Ha
  • 1
  • 1

1 Answers1

0

In short, no. That's a drawback of single page applications. Since the page isn't actually being navigated, you can't use the native beforeunload event, and since you're not using a router you can't customize routing in that respect either. What you can do is wrap these navigation components with a custom component so you don't need to repeat yourself everywhere.

const MyCustomTabComponent = (props) => {
  function navigateIfNotDirty(tabIndex) {
    // Check if it's safe to navigate
    props.onTabChange?.() // Maybe allow passing another callback in as well
    // Finally navigate
  }
  
  return <TabComponent {...props} onTabChange={navigateIfNotDirty} />
}

I don't know the actual syntax of the components you're using, but that should give you the idea.

The actual logic will be specific to the UI library you're using, if any. By your question, it seems like you already figured out how to cancel navigation given a condition, it's just a matter of abstracting that logic into a new, reusable component.

If the navigation components are already custom instead of being from a library, then it's even easier.

Chris Hamilton
  • 9,252
  • 1
  • 9
  • 26
  • Thanks Chris! I am using our private in house UI library. And yes, I can cancel the navigation conditionally to keep user at the current view. I use react-confirm for the confirmation and navigation happens only when user accept by click Yes in the confirm modal. With your suggestion above, we still need to wrap every component that could resulting navigating. So look like there is no quick and easy way to solve this problem :) – Hoang Ha Nov 24 '22 at 19:16
  • Great, then you can just implement checking your redux flag at that component level. You could also just make that flag a property that you pass in, which prevents navigation when true. – Chris Hamilton Nov 24 '22 at 19:23
  • Nope, definitely not quick and easy, but if the components are internal then you can edit them to allow for cancelling navigation given a condition, a simple boolean property that you pass in may be the simplest, something like `suppressNavigation`. But up to you and your team to decide what works best for you. – Chris Hamilton Nov 24 '22 at 19:24
  • Yes I already implemented this conditional navigation with a boolean prop for some of our components like this for the Tab ``` if (confirmOnTabChange && activeIndex !== index) { if (await confirm(confirmOptions)) { changeTab(); } } else { changeTab(); }``` – Hoang Ha Nov 24 '22 at 19:36