Skip to content

Commit

Permalink
Merge pull request #867 from bebbi/feat/before-unload-callback
Browse files Browse the repository at this point in the history
feat(useBeforeUnload): allow passing a dirty function (#842)
  • Loading branch information
streamich authored Feb 15, 2020
2 parents bf986d7 + c4a14a4 commit 5ea486e
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 12 deletions.
27 changes: 27 additions & 0 deletions docs/useBeforeUnload.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ React side-effect hook that shows browser alert when user try to reload or close

## Usage

### Boolean check

```jsx
import {useBeforeUnload} from 'react-use';

Expand All @@ -20,3 +22,28 @@ const Demo = () => {
);
};
```

### Function check

Note: Since every `dirtyFn` change registers a new callback, you should use
[refs](https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback)
if your test value changes often.

```jsx
import {useBeforeUnload} from 'react-use';

const Demo = () => {
const [dirty, toggleDirty] = useToggle(false);
const dirtyFn = useCallback(() => {
return dirty;
}, [dirty]);
useBeforeUnload(dirtyFn, 'You have unsaved changes, are you sure?');

return (
<div>
{dirty && <p>Try to reload or close tab</p>}
<button onClick={() => toggleDirty()}>{dirty ? 'Disable' : 'Enable'}</button>
</div>
);
};
```
27 changes: 18 additions & 9 deletions src/useBeforeUnload.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
import { useEffect } from 'react';
import { useCallback, useEffect } from 'react';

const useBeforeUnload = (enabled: boolean = true, message?: string) => {
useEffect(() => {
if (!enabled) {
return;
}
const useBeforeUnload = (enabled: boolean | (() => boolean) = true, message?: string) => {
const handler = useCallback(
(event: BeforeUnloadEvent) => {
const finalEnabled = typeof enabled === 'function' ? enabled() : true;

if (!finalEnabled) {
return;
}

const handler = (event: BeforeUnloadEvent) => {
event.preventDefault();

if (message) {
event.returnValue = message;
}

return message;
};
},
[enabled, message]
);

useEffect(() => {
if (!enabled) {
return;
}

window.addEventListener('beforeunload', handler);

return () => window.removeEventListener('beforeunload', handler);
}, [message, enabled]);
}, [enabled, handler]);
};

export default useBeforeUnload;
22 changes: 19 additions & 3 deletions stories/useBeforeUnload.story.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import React, { useCallback } from 'react';
import { useBeforeUnload, useToggle } from '../src';
import ShowDocs from './util/ShowDocs';

const Demo = () => {
const DemoBool = () => {
const [dirty, toggleDirty] = useToggle(false);
useBeforeUnload(dirty, 'You have unsaved changes, are you sure?');

Expand All @@ -15,6 +15,22 @@ const Demo = () => {
);
};

const DemoFunc = () => {
const [dirty, toggleDirty] = useToggle(false);
const dirtyFn = useCallback(() => {
return dirty;
}, [dirty]);
useBeforeUnload(dirtyFn, 'You have unsaved changes, are you sure?');

return (
<div>
{dirty && <p>Try to reload or close tab</p>}
<button onClick={() => toggleDirty()}>{dirty ? 'Disable' : 'Enable'}</button>
</div>
);
};

storiesOf('Side effects|useBeforeUnload', module)
.add('Docs', () => <ShowDocs md={require('../docs/useBeforeUnload.md')} />)
.add('Demo', () => <Demo />);
.add('Demo (boolean)', () => <DemoBool />)
.add('Demo (function)', () => <DemoFunc />);

0 comments on commit 5ea486e

Please sign in to comment.