Categories
User Interface

React Prompt on page unload (Typescript)

This React Prompt Component allows you to show a dialog before closing a page or when navigating to another page. This is an extension on the existing prompt component within React which only shows dialog messages when navigating with React Router. This component works in hooks and class components.

Copy paste the code below and save it as DirtyPrompt.tsx

import * as React from 'react';
import { Prompt } from 'react-router-dom';
import { useEffect, useCallback } from 'react';

interface IProps {
	when?: () => boolean | undefined;
	message?: string;
}

export default ({
	when,
	message = "Close page?" }
	: IProps) => {

	useEffect(() => {
		window.addEventListener('beforeunload', beforePageUnload);
		return () => window.removeEventListener('beforeunload', beforePageUnload);		
	}, [when]);

	const beforePageUnload = useCallback((e: BeforeUnloadEvent) => {
		if (when && when()) {
			e.preventDefault();
			return e.returnValue = message;
		}
	}, [when]);

	return <Prompt message={() => when && when() ? message : true} />;
}

Usage:

 <DirtyPrompt when={() => true} message="Close page now?" />
  • when: A callback which determines if the Prompt should be shown or not. Return true to show the dialog prompt when navigating to another page or exiting the page.
  • message (optional): The message to show. This message is not always visible. Some browsers do not allow custom messages in beforeunload events. The browser will instead show a default dialog.
    Default message: “Close page?”

React Example in TypeScript:

import * as React from 'react';
import { useState, useCallback, useEffect } from 'react';
import DirtyPrompt from './DirtyPrompt';

export default () => {

    //state for keeping track of changes.
    const [dirty, setIsDirty] = useState(false);

    //callback for setting form as dirty.
    const dirtyCallback = useCallback(() => dirty, [dirty]);

    //the form values
    const [formValue, setformValue] = useState("");

    //Sets the dirty flag when formValue changes
    useEffect(() => { formValue && setIsDirty(true); }, [formValue]);

    //Callback for state changes.
    const inputChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => setformValue(e.target.value);

    //Render component
    return <>
        <DirtyPrompt when={dirtyCallback} message="Proceed closing this page?" />
        <label>
            Testinput:
            <input type="text" onChange={inputChangeHandler} value={formValue} />
        </label>
        <p>Input is dirty Test: {dirty ? 'true' : 'false'} </p>
    </>
}

Example for DevExtreme (DevExpress) DataGrid in BatchMode:

import * as React from 'react';
import { DataGrid, Editing } from 'devextreme-react/data-grid';
import DataSource from 'devextreme/data/data_source';
import { useRef, useCallback } from 'react';
import DirtyPrompt from './DirtyPrompt';

export default () => {
    const gridRef = useRef<DataGrid>(null);
    const dataSource: DataSource = createStore( ... );
    const dirtyCallback = useCallback(() => gridRef.current?.instance.hasEditData(), [gridRef]);    
    return <>
        <DirtyPrompt when={dirtyCallback} />
        <h3>Categorieën</h3>
        <DataGrid ref={gridRef}>            
            <Editing
                mode="batch"
                useIcons={true}
                startEditAction="dblClick"                                        
                allowAdding={true}                    
                allowDeleting={true}
                allowUpdating={true} />
            ...
        </DataGrid>
    </>
}

Leave a Reply

Your email address will not be published. Required fields are marked *