How to handle onclicks on hyperlinks most gracefully - reactjs

What I want is rendered HTML that looks like this My cool Slug.
That means that someone can right-click on the link and open in a new tab. That'll bypass the fancy pushState stuff that react-router provides.
However, if someone makes a regular click, I need to update redux to say what the new slug is (or pagination page or whatever) AND call browserHistory.push(...).
There has to be a simpler way that is convenient. Something that does almost all of this without all the mess. Here's what my application looks like:
// imports
import { browserHistory } from 'react-router'
import changeSlug from './actions'
// the function
makeURL(thing) {
return `/page/${thing.slug}`
}
// this click method
handleClick(event, thing) {
event.preventDefault()
this.props.dispatch(changeSlug(thing.slug))
browserHistory.push(`/page/${thing.slug}`)
}
// the JSX
<a href={makeURL(myobject)}
onClick={(event) => handleClick(event, myobject)}
>Go to {myobject.title}</a>
Also, I tried using event.target in the event handler to get to the a.href attribute but because the <a> tag contains <span> elements, then event.target is the <span> tag I clicked inside the <a> element.

You can use <Link /> from react_router and determine onEnter/etc.
actions in your <Route /> (in <Router />).
Also you can wrap
your <a> in <span> and add some attributes to it and handle
click event.
Also you can add display: block style to <a> => it
shouldn't wraps by <span>

Related

history.goBack doesnt't work on iOS devices, how to fix that?

I made a go Back button on a page which moves user from current page to a previous one. It works fine on PC and Android devices but it doesn't on iOS. Unfortunatelly I don't have a Mac to take a look at devtools to understand what's happening. Might be someone knows how to fix that? I suspect that the reason might be in useParams and React Router, but I don't know...
GoBack.tsx
interface IGoBackLinkProps {
text: string;
}
const GoBackLink: React.FC<IGoBackLinkProps> = ({ text }) => {
const history = useHistory();
return (
<Link className={styles.link} to='/' onClick={() => history.goBack()}>
{text}
</Link>
);
};
export default GoBackLink;
I use this component in this way:
SomeOtherCOmponent = () => {
const {id} = useParams
...
return (
<GoBackLink text={'Назад'} />
...
)
}
The <Link /> component from react-router-dom, by default, will render an <a> tag and it will use the path from the to prop as the href. So your Link is probably rendering something like this but with an onClick event handler:
<a href="/">
Go Back
<a>
When the user clicks this link, the onClick event is dispatched and then the anchor tag takes you to '/'.
One way to fix this would be to just use a button instead of Link:
<button onClick={() => {history.goBack()}}>
Go Back
</button>
If you must use the Link component or an a tag, e.preventDefault() in your onClick handler may also work.

Make child element of Link not route

I'm coding an application where people can make posts, and comment on them.
All of the posts are displayed on one page.
If a post is clicked the user gets routed to that post's page, which contains the full post, more details, and all of that post's comments.
Currently, I have the post div wrapped in a react-router Link. That works swell, except that when I click a button inside that div, I still get routed.
I want everywhere in the div to be "clickable", except actions like other Links or buttons.
<Link to={`/${_id}`}>
<div>
<button>I don't want this button to route</button>
</div>
</Link>
I'm not sure if there is a way to do this, but I can't find anything on it. I found one guy on another forum asking the same thing, but it was old and never got answered. Maybe I'm missing it, but I can't find it in the react-router docs. Maybe react-router is not even capable of this, idk?
Something like a quickfix but I don't know if its the best way to address this. I am looking first at the useRef but no avail.
Using something like e.preventDefault on the onClicks will probably save a lot of headache
<Link to="/">
<h1>Home</h1>
<button onClick={(e) => e.preventDefault()}>HAIYAAA</button>
</Link>
On hindsight this seems to be a Event Bubbling Issue right there.
See a fiddle here: https://codesandbox.io/s/react-router-forked-5rj2b?file=/index.js
EDIT:
Probably a much more dynamic way is to add a condition at the Link's OnClick and exclude the nested A (don't nest it otherwise React will nag you about it) and Button.
Basically we are dealing with a event bubbling here so might just use it to fine grain the behavior that we want. clicking anywhere inside the div will result to being routed to a page, while clicking any A or Button will not
<Link
to="/"
onClick={(e) => {
if (e.target !== e.currentTarget) {
if (["A", "BUTTON"].includes(e.target.nodeName)) {
e.preventDefault();
}
}
}}
>
<div>
Home
<br />
<button}>
HAIYAAA Don't Route me
</button>
<br />
<a href="#">
This is a link that is nested (Its not good to nest a!)
</a>
</div>
</Link>
Fiddle here: https://codesandbox.io/s/react-router-forked-4m4ww?file=/index.js
PS : ahh I spent time to learn something. Thanks for making my afternoon not a sleepy one
We need to stop the propagation of onClick event of the child to its parent.
<Link to="/">
<h1>Home</h1>
<button onClick={(e) => {
myNeededBehaviour();
e.stopPropagation();
}}>
HAIYAAA
</button>
</Link>
so, the stopPropagation will stop the event from propagating to its parent, and it will still calls myNeededBehaviour(); if you want, but the parent wont get the child's event. so it wont route, as it is not getting triggered.

System to manage reactjs applications

I get how to specify where to render the reactjs application by using the render method and specifying the html tag where it should be rendered.
What I do not understand is how you can have a list of react.js applications that is dynamically loaded into that same HTML tag.
For example there is a sidebar which is dynamically created to give a user a list of N number of react.js applications. When the user clicks on one of the links it loads that application into the HTML tag (div or whatever) container on the right.
I am sure this may be something easy but have been struggling with this concept for awhile.
Would appreciate any inputs anyone has on this.
If you truly had multiple full apps you wanted to swap out, you'd have to manually mount and unmount them. Something like a function like this, that unmounts the previous app, then mounts a new one. Example
function swapApp(App) {
const appNode = document.getElementById('app')
ReactDOM.unmountComponentAtNode(appNode)
ReactDOM.render(<App />, document.getElementById('app'))
}
But that would be a pain. So, typically, that menu and the content being changed are all part of the same react app. This app would render the menu, keep state about what item you clicked, and then render some components conditionally, depending on what was clicked.
Something like this example
function App() {
const [showingItem, setShowingItem] = React.useState(null)
return (
<>
<p><a href="#" onClick={() => setShowingItem('A')}>Show Item A</a></p>
<p><a href="#" onClick={() => setShowingItem('B')}>Show Item B</a></p>
{showingItem === 'A' ? <AppA /> : null}
{showingItem === 'B' ? <AppB /> : null}
</>
)
}

Link changing URL but not the page

I am using Ant design breadcrumbs. I am trying to change the page using link and pushing the URL, I can see the URL change but the page in not changing.
I tried using Link, then creating a function for onClick but everything just change the URL.
Route:
<Route exact path="/assistant/:wId/skill/xyz/:sid" component={ xyz } />
Tried process 1:
<Breadcrumb separator=">">
<Breadcrumb.Item
onClick={this.redirectToParam2}>
{param2}
</Breadcrumb.Item>
</Breadcrumb>
redirectToParam2 = () => {
this.props.history.push(`/assistant/${wId}/skill/xyz/${sId}`);
}
Tried process 2:
<Breadcrumb separator=">">
<Breadcrumb.Item>
<Link to= {`/assistant/${wId}/skill/xyz/${sId}`}>
{param2}
</Link>
</Breadcrumb.Item>
</Breadcrumb>
Even I tried without the Breadcrumbs component but it's still not changing the page.
I want the page to change as soon as the URL changes.
Thank you in advance.
Try this,
import { Link } from "react-router-dom";
<Breadcrumb separator=">">
<Breadcrumb.Item>
<Link to= {`/assistant/${wId}/skill/xyz/${sId}`}>
{param2}
</Link>
</Breadcrumb.Item>
</Breadcrumb>
The problem you are running into, is that with changing the parameters used as props for the xyz component, the component is not replaced but gets new properties. Since nothing changes, i'm assuming you have state that gets filled either in the constructor or ComponentWillMount/ComponentDidMount.
React class components have a lifecycle function for this: componentDidUpdate.
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
Quote from react docs, See: https://reactjs.org/docs/react-component.html#componentdidupdate
componentDidUpdate(prevProps) {
if ((this.props.params.match.sid !== prevProps.params.match.sid) ||
(this.props.params.match.wid !== prevProps.params.match.wid)) {
this.populateState(this.props.params.match); //fill your state
}
}

Using React Router with different tag

React Router has a Link component with generates an HTML <a> element.
I need to generate an <area href=... >.
How can I do it?
You can have Link as child to your component.
<area>
<Link
to={'/path/to/somehwhere}'}>
Click Me
</Link>
</area>
Or you can specify a onclick event handler on area and use hashHistory or browserHistory from react-router to manually navigate user to desired location.
<area onClick={handleClick}>
// your code here
</area>
and handleClick will have
handleClick = (): void => {
hashHistory.push('/path/to/somehwhere'); // or browserHistory
}
In these both ways you can achieve your desired behavior. You can read more about navigating users programmatically here.
Edit: I forgot to mention that you can also wrap your whole component inside Link.
<Link to='/path/to/somewhere'>
<area />
</Link>

Resources