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
Related
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.
Currently I have a navbar component that stays at the top for all the pages except the home page. To achieve this I used conditional rendering using useEffect and useState where I check if the current URL is the home page or not.
Code:
const [ishome,setIsHome] = useState(false);
useEffect(function onFirstMount() {
function onLoad() {
const url= window.location.href;
if(url === "http://localhost:3000/home"){
setIsHome(true)
}
else{
setIsHome(false)
}
}
window.addEventListener("load", onLoad);
}, []);
return (
<div className="fullNav" style={{marginTop:ishome?"100vh":""}}>
But the problem with this is that everytime this page loads I can see the navbar at the top of the home page for a split second and then it goes down. I dont want it to be shown at the top of the homepage everytime the user clicks refresh.
You are checking your location in useEffect(with an empty dependency array). This essentially means you are checking it in componentDidMount. So that is after the first render. Probably that is why your styling is applied later.
If you want it to happen as soon as it is rendered, why not call the function while setting state.
Something like this:
export default function App() {
const [ishome,setIsHome] = useState(function onLoad() {
const url= window.location.href;
if(url.indexOf("x")){
return 'true';
}
else{
return 'false';
}
});
useEffect(() => {
}, []);
return (
<button>{ishome}</button>
)
}
Sandbox
this is the type of back button that is used to navigate to the previous pages, rather than using the navigation bar but I now want to disable it when the next previous route inline is 'login'
I've read other questions on this platform and tried this but it seems like it's not the right logic
const handleGoBack = () => {
if (authStatus) {
window.history.pushState(null, null, location.href);
window.onpopstate = function (event) {
window.history.go(1);
};
return window.history.back()
};
};
I doubt if there is such a thing but I want something of this nature
const handleGoBack = ( e, authStatus ) => {
if(previouseRouter === 'login')
return authStatus && event.currentTarget.disabled === true
}
is there a better way to tackle the problem.. and also I'm using Hashrouter
Instead of check if previous router was Login, you could navigate from Login to the next page using history.replace("path/to/next/page"). In this way you replace last navigation history and goBack does not falls into Login page.
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
I'm trying to create a simple component with React (I'm a new user), and I have some troubles to show and hide div. I'm using a state to handle a click and change state, which works fine. Problem is when I'm using the back button from the browser, and come back on the main page, I've no clue how to handle state change as there is no interaction with the user.
I tried to use the location context to change state if the URL pathname === "/", but it looks like anti react pattern because I have to force the component to rerender and check the pathname inside the initial state function. Any ideas how to handle this case?
// DIV on the main page
const Div = React.createClass({
/*contextTypes: {
location: React.PropTypes.object
},*/
getInitialState: function() {
console.log("get initial state");
return { hideDiv: false };
},
handleClick(){
this.setState({ hideDiv: true });
},
render() {
console.log(this.state.hideDiv);
let componentDOM;
if(this.state.hideDiv === true){ componentDOM = <div></div>;}
else{
componentDOM = <div id='showHide'>
<form>
<div>
<select>
<option> ... </option>
</select>
</div>
//Link to a second page
<button type='submit' onClick={this.handleClick}> <Link to {'/destination'}>Submit</Link></button>
</form>
</div>;
}
return (componentDOM);
}
});
I would advise against storing the information about whether or not the component with the form is visible in its own state. From your description, it seems to me like this information belongs higher in the hierarchy - the Div component itself is not capable of deciding whether or not it should be visible, as that depends on some context (URL / application phase) unknown to it.
I'd recommend something like this:
var App = React.createClass({
//Data for the form, you might want to keep them in a store
getInitialState(){ return {data: {}}; }
render(){
//Pass data from your routing library to the props of App
if(this.props.routingParams.url === 'form')
return <Div data={this.state.data} onDataChanged={...} />
else
return <Destination data={this.state.data} />
}
});
Plus remove the state and the hiding logic from Div completely.
To do this you should save your state, using localstorage for example, like this:
handleClick: function(e) {
this.setState({hideDiv: true});
var state = this.state; // we need to add hideDiv with new value because setState could not update instantly
state.hideDiv = true;
localStorage.setItem('MyDivComponent', JSON.stringify(state));
}
And then, when a component mount, get default state:
getInitialState: function() {
var state = JSON.parse(localStorage.getItem('MyDivComponent')) || {};
return {
hideDiv: state.hideDiv || false
};
}