Merge pull request #867 from bebbi/feat/before-unload-callback

feat(useBeforeUnload): allow passing a dirty function (#842)
This commit is contained in:
Vadim Dalecky 2020-02-15 14:10:30 +01:00 committed by GitHub
commit 5ea486edf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 12 deletions

View File

@ -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';
@ -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>
);
};
```

View File

@ -1,12 +1,14 @@
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) {
@ -14,12 +16,19 @@ const useBeforeUnload = (enabled: boolean = true, message?: string) => {
}
return message;
};
},
[enabled, message]
);
useEffect(() => {
if (!enabled) {
return;
}
window.addEventListener('beforeunload', handler);
return () => window.removeEventListener('beforeunload', handler);
}, [message, enabled]);
}, [enabled, handler]);
};
export default useBeforeUnload;

View File

@ -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?');
@ -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 />);