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
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
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 have a problem with react-router updating props everytime the component made route change.
In my document, I have multiple sections, and all sections are linked to navigation menu.. such as:
/about#aboutus
/about#contact
In the about component, I implemented a scrollSpy function like this:
scrollSpy() {
if (this.isTransitioning) return;
let hash = this.getCurrentSection();
if (this.state.activeSection != hash) {
this.setState({ activeSection: hash });
history.pushState(null, null, hash);
}
}
and also scrollTo function like this
scrollTo(hash) {
this.isTransitioning = true;
let section = $(hash);
let body = $("html, body");
let navBar = $("#mainNav");
let top = section.offset().top - navBar.height() - 10;
body.stop().animate({
scrollTop: top
}, 800, 'swing', () => {
this.isTransitioning = false;
this.scrollSpy();
})
}
I also implement this function to scroll article to intended section when chosen from at navigation bar.
componentDidUpdate(prevProps, prevState) {
if (this.props.location.hash != prevProps.location.hash) {
this.scrollTo(this.props.location.hash);
}
}
Now, the problem is, when I insert a line to update history into scrollSpy like this:
scrollSpy() {
if (this.isTransitioning) return;
let hash = this.getCurrentSection();
if (this.state.activeSection != hash) {
this.setState({ activeSection: hash });
history.pushState(null, null, hash);
this.props.history.push(hash);
}
}
System would scroll indefinitely, up and down.
It seems the component send signal to React-Router, and react-router change props, and scrollSpy again send back to react-router. So it kind of stuck in a loop. Adding a variable such as isUpdating = true didn't help, and made its behavior unpredictable. Sometimes it will scroll, sometimes it just won't, sometimes it jumped back to top.
How can I determine whether props change comes from my history.push trigger or a user clicking navigation bar? I want the page to scroll to desired top of section when user clicks on the navigation bar, even when it is the current section shown on screen.
Thank you
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
};
}