I have an a link in the return section of a react component and when
I click on the link the whole page reloads. I don't want the whole page
to reload. How can I correct this?
const Tester = () => {
...
...
...
if (showComponent) {
return (
<div className={classes.root}>
<div className={classes.messageWrap}>
<a
href=''
onClick={() => toggle()}
className={classes.messageText}
>
{userType}
</a>
</div>
</div>
)
}
return null
}
Your click handler will receive an event argument. You can prevent the event from propagating up and/or prevent the default behavior (in this case, navigating) by calling event.stopPropagation() and event.preventDefault() respectively.
const handleClick = e => {
e.preventDefault();
// do other stuff.
}
<a href='' onClick={handleClick}>...</a>
I would suggest, however, that a <button> makes more sense semantically if you're not using it to navigate.
const handleClick = () => {
// do stuff. no need to fight the default browser behavior.
}
<button onClick={handleClick}>...</button>
Related
While trying to get a DOM element's position on render using this code:
const Modes = () => {
const callbackRef = useCallback(domNode => {
if (domNode) {
let rect = domNode.getBoundingClientRect()
console.log("rect", rect)
}
}, []);
return <>
<Toast>
<ToastHeader></ToastHeader>
<ToastBody>
<div ref={callbackRef}> </div>
</ToastBody>
</Toast>
</>
}
I noticed that it always prints a DOMRect object with zero for every property :
If I add state dependence and then state changes causing rerender, the correct position will be printed. Something like this:
const Modes = () => {
const callbackRef = useCallback(domNode => {
if (domNode) {
let rect = domNode.getBoundingClientRect()
console.log("rect", rect)
}
}, []);
const [show, setShow] = useState(true) // added state
return <>
<Toast>
<ToastHeader></ToastHeader>
<ToastBody>
{show ? <div ref={callbackRef}> </div> : null} // div inside Toast can be toggled
</ToastBody>
</Toast>
<Button onClick={() => setShow(!show)} >toggle </Button> // added toggle button
</>
}
After double click on the button:
What confuses me the most is the fact that if I replace this Toast imported from Reactstrap with pure html with bootstrap classes the problem disappears. And this is exactly what React renders because I copied it from source code in the browser:
<div class="toast fade show" role="alert">
<div class="toast-header">
<strong class="me-auto"></strong>
</div>
<div class="toast-body">
<div ref={callbackRef}> </div>
</div>
</div>
And it seems to be a problem that exists just for this Toast component. For Reactrstrap's Card for example it is not the case. So how can using a component which at the end of the day gets rendered into a certain html code be different from using the same html code and why this particular component turns out to be a special case regarding obtaining its DOMRect?
how can I show a video when the button is clicked and then it performs an
action , so step by step:
user lands to the main page
user clicks on the button that should take him/her to home page
I used useNavigate to change the route of the page to /home, but before doing that, I want the user to see short video.
so far this is what I came up with, but it doesnt work
const Main = (props) => {
let navigate = useNavigate();
const playVideo = () => {
return (
<div>
<video src={video}></video>
</div>
)
}
const handleClickHome = async (event) => {
event.preventDefault();
await playVideo()
return (
navigate("/home")
)
}
return (
<div className={classes.main} >
<button onClick={handleClickHome} className={classes.button}> <h2> LET ME IN </h2> </button>
</div >
);
}
export default Main ```
You can show your video by clicking the button and use under below event for navigation.
document.getElementById('videoId').addEventListener('ended',myHandler,false);
function myHandler(e) {
// do navigation
}
Context: I am trying to scroll view to props.toBeExpandItem item which keeps changing depending on click event in parent component. Every time a user clicks on some button in parent component I want to show them this list and scroll in the clicked item to view port. Also I am trying to avoid adding ref to all the list items.
I am using react ref and want to add it conditionally only once in my component. My code goes as below. In all cases the option.id === props.toBeExpandItem would be truthy only once in loop at any given point of time. I want to understand will it add any overhead if I am adding ref=null for rest of the loop elements?
export const MyComponent = (
props,
) => {
const rootRef = useRef(null);
useEffect(() => {
if (props.toBeExpandItem && rootRef.current) {
setTimeout(() => {
rootRef.current?.scrollIntoView({ behavior: 'smooth' });
});
}
}, [props.toBeExpandItem]);
return (
<>
{props.values.map((option) => (
<div
key={option.id}
ref={option.id === props.toBeExpandItem ? rootRef : null}
>
{option.id}
</div>
))}
</>
);
};
Depending upon your recent comment, you can get the target from your click handler event. Will this work according to your ui?
const handleClick = (e) => {
e.target.scrollIntoView()
}
return (
<ul>
<li onClick={handleClick}>Milk</li>
<li onclick={handleClick}>Cheese </li>
</ul>
)
I am trying to generate a list of buttons using this method. I rewrote it as a test instance and the behaviour is the same - when you render the buttons, the function in onClick is called and cannot be called again by clicking the generated buttons. In this case 1 to 5 are logged.
function App() {
const [buttonList, setButtonList] = useState();
const experimental = async (e) => {
const array = [1,2,3,4,5];
const listItems = array.map((item) =>
<li key={item}>
<button onClick={console.log(item)}>This is a test</button>
</li>
);
setButtonList(listItems);
}
return (
<div className="App">
<header className="App-header">
<button onClick={experimental}>Click to render</button>
<ul>{buttonList}</ul>
</header>
</div>
);
}
Could anyone explain this behaviour to me and/or offer a way of doing this which doesn't do this?
Due to context this. It's must be () => {}. Let's use function and useCallback hook instead.
In the following component, if I click on any of the two buttons, the URL in the address-bar gets changed.
In the list view if you click on the button Details the page get rendered and shows that particular item and the URL in the address bar get changed too.
And in the user view if you click on the "Back to overview" button, the page renders back to the list view and the URL gets changed again.
import React, { useState, useEffect, Fragment } from 'react'
import axios from 'axios'
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
const renderItem = (item, userId) => {
const setURL = (e) => {
window.history.pushState(null, null, '/' + e)
setID(item.login.uuid)
}
const clearURL = (e) => {
window.history.back()
setID(null)
}
return (
<Fragment key={item.login.uuid}>
{userId ? (
// User view
<div>
<img src={item.picture.large} />
<h2>
{item.name.first}
</h2>
<p>
{item.phone}
<br />
{item.email}
</p>
<button onClick={() => clearURL('/')}>
Back to overview
</button>
</div>
) : (
// List view
<li>
<img src={item.picture.large} />
<h2>
{item.name.first}
</h2>
<button onClick={() => setURL(item.login.uuid)}>
Details
</button>
</li>
)}
</Fragment>
)
}
const user = resources.find(user => user.login.uuid === id)
if (user) {
// User view
return <div>{renderItem(user, true)}</div>
} else {
// List view
return (
<>
<ul>
{resources.map(user => renderItem(user, false))}
</ul>
</>
)
}
}
export default UserList
Everything is working fine.
However, the problem with this solution is that on user view, I cannot use the browsers back button to go back to the list view page.
Is there any way I can change this without using React Route?
So what I believe is happening is you are over-writing the 'history' of the browser. The 'history' is pretty much just a stack of paths you've been and when you click the back button it just pops the current path off the stack. I think that when your using 'window.history.pushState(null, null, '/' + e)' its setting the history = to null so there is nothing to go back to. I would recommend using react-router and just pushing a path ie. router.push('/whatever'). There are a bunch of ways to interact with the history through react without using the native history object. If you want to use the native history object https://developer.mozilla.org/en-US/docs/Web/API/History_API
Edit - I apologize your not overwritting the history, but I do still believe that the error is coming from passing a 'null' value as the 'state' param for the pushState method