I would like to show the popup only one time with React Hooks.
Access for the first time to example.com/campaign/1234
Show popup
Close or refresh the page.
Access again to example.com/campaign/1234 and don't show popup
Access for the first time to example.com/campaign/0000 (is a different URL)
Show popup
Close or refresh the page
Access again to example.com/campaign/0000 or example.com/campaign/1234 and the popup is not being displayed
Any idea of how to do it? I know that I need to use local storage but how can I trigger the event when the user closes or refreshes the page?
Here is a sandbox.
I also read this thread but it doesn't mention how to do it with Hooks
If you never use the setStickyState callback from the custom hook, the state will just remain at its initial value.
It seems like setStickyState also has a bug in it, where it won't update if the key has changed. Here's an enhanced version that I've called useLocalStorage, which should work more reliably:
export function useLocalStorage(key, initialDefault) {
const [val, setVal] = useState(() => {
const localStorageVal = localStorage.getItem(key);
return localStorageVal !== null
? JSON.parse(localStorageVal)
: initialDefault;
});
useEffect(() => {
if (localStorage.getItem(key) === null) {
setVal(initialDefault);
}
}, [key, initialDefault]);
useEffect(() => {
localStorage.setItem(key, JSON.stringify(val));
}, [val, key]);
return [val, setVal];
}
You can then use it like this:
const [visited, setVisited] = useLocalStorage(pageId, false);
const navigateAway = useCallback(() => {
setVisited(true)
}, [setVisited])
useEffect(() => {
// if user navigates away to a completely different site
// or refreshes the page etc
window.addEventListener("beforeunload", navigateAway);
// if user navigates to another page on the same site
return () => {
navigateAway();
window.removeEventListener("beforeunload", navigateAway);
};
}, [pageId, navigateAway]);
// ...
<dialog open={!visited}>
<p>Welcome to page {pageId}!</p>
<button onClick={() => setVisited(true)}>
Don't show again on this page
</button>
</dialog>
Here's a demo (with TypeScript):
useLocalStorage demo
Related
I'm currently working on a project where I want to show a custom Dialogue box with my Own Content ("Save your data into drafts before leaving"). I have tried different methods but can't find a proper way to do it. I explore all the previous questions on StackOverflow but they didn't work properly in my case.
useEffect(() => {
return () => {
window.onbeforeunload = function() {
return "do you want save your data into drafts before leave?";
};
}
},[])
Currently, I've written the above code in Plain JavaScript to do, but it's just showing the dialogue box on tab close and reload while not showing on custom click events to navigate to other pages or window back button.
React can't help me in this because they remove useBlocker, usePrompt from new releases. How can I achieve it?
One way of doing this is :
import { Prompt } from 'react-router'
const MyComponent = () => (
<>
<Prompt
when={shouldBlockNavigation}
message='Do you want ot save data before leave?'
/>
{/* Component JSX */}
</>
)
If wants on page refresh or browser closing then add:
useEffect(() => {
if (shouldBlockNavigation) {
window.onbeforeunload = () => true
} else {
window.onbeforeunload = undefined
}
},[]);
Second way is to use history if using react-router
useEffect(() => {
let unblock = history.block((tx) => {
// Navigation was blocked! Let's show a confirmation dialog
// so the user can decide if they actually want to navigate
// away and discard changes they've made in the current page.
let url = tx.location.pathname;
if (window.confirm(`Are you sure you want leave the page without saving?`)) {
// Unblock the navigation.
unblock();
// Retry the transition.
tx.retry();
}
})
},[]);
useEffect(() => {
const unloadCallback = (event) => {
event.preventDefault();
event.returnValue = "";
return "";
};
window.addEventListener("beforeunload", unloadCallback);
return () => {
window.addEventListener("popstate", confirmation());
window.removeEventListener("beforeunload", unloadCallback);
}
}, []);
I just did it with this code sample (actually I combine two events to show dialogue whenever users leave a page) and it's working fine for me. Thanks to all of you guys ... Especially #DrewReese for the help
I am having a button, and when I click on it, it should do the following things in order:
First refresh the Page
Then Open the Side Pane in React
This is my code snippet that is existing:
<button onClick={() => {
refreshPage();
setPaneIsOpen(true);
}}>Click me</button>
const refreshPage = () => {
window.location.reload();
}
setPaneIsOpen is a State that when set to true, will open the side pane. I want to specifially first refresh the page and then set the Pane.
I have tried this async/await promise approach:
function refreshPagePromise() {
return new Promise<void>((resolve) => {
window.location.reload();
resolve();
});
}
async function refreshAndOpen() {
await refreshPagePromise();
setIsPaneOpen(true);
}
<button onClick={() => { refreshAndOpen(); }}>Click me</button>
But I cannot seem to handle page reload. I tried to create a Code Sandbox, but since this is very complex, I could not. I will try to add one in the edits If I was able to reproduce.
One hacky way would be to use localStorage.
add this useEffect that runs on page load:
useEffect(() => {
const shouldOpenPane = !!localStorage.getItem("setPaneOpen")
if(shouldOpenPane){
setIsPaneOpen(true);
// Maybe the row beneath should not be there,
// Idk what behaviour u want if user reloads page manually
localStorage.removeItem("setPaneOpen")
}
}, []);
Update your button like this:
<button
onClick={() => {
localStorage.setItem("setPaneOpen", "true"); // can be set to any thruthy value
refreshPage();
}}
>
Click me
</button>
I am trying to Differentiate Between Page Refresh, Browser Close and New tab events.
So, I want some handling on page close V/s page refresh/new tab
I came across below workaround using sessionStorage. However the issue with sessionStorage is that it gets reset or is not read even on opening link in new tab. But I want both page refresh/new tab to behave in same way V/s refresh of the page.
if (sessionStorage.getItem('reloaded') != null) {
console.log('page was reloaded');
} else {
console.log('page was not reloaded');
}
sessionStorage.setItem('reloaded', 'yes');
You'll have to use a combination of sessionStorage and localStorage to persist the data and rely on beforeunload event to handle the data removal.
The thing is beforeunload fires on both tab/window close and page refresh so we have to work around that.
localStorage will handle persistence across tabs and windows and sessionStorage will sync the data on page refresh.
const readFromStorage = (storageKey) => {
const localStorageItem = localStorage.getItem(storageKey);
const sessionStorageItem = sessionStorage.getItem(storageKey);
// You can optimize this by doing more checks but you get the idea
const itemValue = localStorageItem ?? sessionStorageItem;
if (localStorageItem !== sessionStorageItem) {
writeToStorage(storageKey, itemValue);
}
return itemValue;
};
const writeToStorage = (storageKey, value) => {
localStorage.setItem(storageKey, value);
sessionStorage.setItem(storageKey, value);
};
Event handler:
window.addEventListener('beforeunload', (e) => {
localStorage.removeItem(STORAGE_KEY);
});
Usage:
const STORAGE_KEY = '<storage_key>';
const item = readFromStorage(STORAGE_KEY);
If item is null - a tab/windows was closed. Otherwise, the data will persist across refreshes and new tabs/windows.
I'm using React-Router to pass value from a page to another.
I have two Page: PageA and PageB
In the PageA I have add a button to go in the PageB, passing in the state a value:
<Button tag={Link} to={{pathname: `/pageB`, state: `${value.id}`}} replace color="primary">
<span className="d-none d-md-inline">PageB</span>
</Button>
In the PageB:
useEffect(() => {
if(props.location.state){
console.log("props.location.state ", props.location.state)
filterWithValue(props.location.state)
}
else {
filter();
}
}, [paginationState.activePage, paginationState.order, paginationState.sort]);
const dataFromValue= parameter => {
const params = `id.equals=${parameter}`
props.getDataFromValue(
params,
paginationState.activePage - 1,
paginationState.itemsPerPage,
`${paginationState.sort},${paginationState.order}`)
}
const filterWithValue= params => {
dataFromValue(params)
const endURL = `?page=${paginationState.activePage}&sort=${paginationState.sort},${paginationState.order}${params ? '&' : ''}id.equal=${params}`;
if (props.location.search !== endURL) {
props.history.push(`${props.location.pathname}${endURL}`);
}
}
Basically in the pageB I check if I come from the pageA and so if I have value props.location.state I will use it to filter the data in the pageB using this value.
If I don't have the value (so I go in the pageB from another place) I call the filter() that shows all the data without filter for value.
Now my problem is that: if i reload the page or click back from the other page
I basically lose the props.location.state and therefore filter () is always called.
How could I go about saving this value? So that if you refesh the page it stays with props.location.state
You can use Window.localStorage for it.
See: https://www.w3schools.com/jsref/prop_win_localstorage.asp
You could use localStorage or sessionStorage.
sessionStorage.setItem("urlState", state);
const urlState = sessionStorage.getItem("urlState");
Main difference between local and session storages, that session will live only while browser not closed
I am new to react.js.
I have a simple page with table. When I reload the page, state is getting lost.
Is there a way to detect the browser refresh ?
The event beforeunload is executed just before window browser is being refreshed. Event is cancellable.
window.beforeunload = (e) => {
console.log('Stop this');
e.preventDefault()
e.returnValue = '';
};
When using React an option is to take control in your Application component, or your most higher order component, in the componentDidMount lifecycle method as #georgim suggested instead componentWillUnmount as I first suggested, and manage there what you want to achieve.
With [react-beforeunload][1] you can track the page changes easily
import { useBeforeunload } from 'react-beforeunload'
const App = () => {
const [preventMultiSubmit, setPreventMultiSubmit] = useState(false)
}
const pageRefConf = useBeforeunload((event) => {
if (preventMultiSubmit) {
event.preventDefault()
}
})
useEffect(() => {
window.addEventListener('beforeunload', pageRefConf)
return () => {
window.removeEventListener('beforeunload', pageRefConf)
}
}, [])
This worked for me. I hope this one will work for you too.
[1]: https://www.npmjs.com/package/react-beforeunload
Try this one. This worked for me.
if (window.performance) {
if (performance.navigation.type == 1) {
alert( "This page is reloaded" );
} else {
alert( "This page is not reloaded");
}
}