I am using react-router-dom Link
and NavLink
v5 and want to short circuit the to
parameter values to pass to the onClick
.
Here is my situation:
In my application - so far only the to
parameter is used for react Link and NavLink.
However I have a new requirement that I need to send some events to the outer iFrame while clicking the Link or NavLink.
So far my code was like this:
const addVolumeLink = location => ({
pathname: location.pathname + "/add-volume",
state: {
from: location.pathname
}
});
<Link to={addVolumeLink}>New volume</Link>
However when user is clicking on the New Volume, I also need to send an event to the outer iFrame. So naively I altered this code as follows by adding the onClick
listerner.
const addVolumeLink = location => ({
pathname: location.pathname + "/add-volume",
state: {
from: location.pathname
}
});
export const nagivateToParentiFrame = (payload) => {
window.parent.postMessage({
type: 'SERVICE:NAVIGATE',
payload,
}, "*")
};
<Link to={addVolumeLink}
onClick={() => nagivateToParentiFrame({
pathname: `${window.location.pathname}/add-volume`,
state: {from: window.location.pathname}})}>
New volume
</Link>
What I am looking for: if there is a way to short-circuit the to
parameter to the onClick. (note: it's a function call here that uses location .. can be a function call or can be an object as well).
Reason: There are 100 of places this onClick in the code I need to write.
So I am thinking of having my own Link component and just replace the import
instead of everywhere writing this onClick (as my pathname and state shall be same).
Something like this I tried (which did not worked out):
// The component which is not working.
import {Link} from "react-router-dom"
const nagivateToParentiFrame = (payload) => {
window.parent.postMessage({
type: 'SERVICE:NAVIGATE',
payload,
}, "*")
};
export const StyledLinkWithIframeEvent = ({ children, className, style, color, type, large, disabled, tooltip, to, ...rest }) => {
return <Link
to={to}
onClick={() => nagivateToParentiFrame(to)}
children={children}
className={className}
style={style}
color={color}
type={type}
large={large}
disabled={disabled}
tooltip={tooltip}
rest={rest}>
</Link>
}
The usage of the component:
import {StyledLinkWithIframeEvent as StyledLink} from "./myLink"
const addVolumeLink = location => ({
pathname: location.pathname + "/add-volume",
state: {
from: location.pathname
}
});
const myMainComponent = () => {
return (<StyledLink to={addVolumeLink}>
New volume
</StyledLink>);
}
Create a custom link component that adds its own onClick
handler that calls your navigateToParentiFrame
function and is passed the result of the passed to
prop.
export const navigateToParentiFrame = (payload) => {
window.parent.postMessage({
type: 'SERVICE:NAVIGATE',
payload,
}, "*")
};
import { Link as BaseLink, useLocation } from 'react-router-dom';
import { navigateToParentiFrame } from '../path/to/navigateToParentiFrame';
const Link = props => {
const location = useLocation();
return (
<BaseLink
{...props}
onClick={navigateToParentiFrame.bind(
null,
typeof props.to === "function"
? props.to(location)
: props.to
)}
>
{props.children}
</BaseLink>
);
};
const addVolumeLink = location => ({
...location,
pathname: location.pathname + "/add-volume",
state: {
...location.state,
from: location.pathname,
}
});
<Link to={addVolumeLink}>New volume</Link>
const {
BrowserRouter: Router,
Switch,
Route,
Link: BaseLink,
useLocation,
} = ReactRouterDOM;
const navigateToParentiFrame = (payload) => {
console.log("window.parant.postMessage", {
type: 'SERVICE:NAVIGATE',
payload,
}, "*");
};
const Link = props => {
const location = useLocation();
return (
<BaseLink
{...props}
onClick={navigateToParentiFrame.bind(
null,
typeof props.to === "function"
? props.to(location)
: props.to
)}
>
{props.children}
</BaseLink>
);
};
const addVolumeLink = location => ({
...location,
pathname: location.pathname + "add-volume",
state: {
...location.state,
from: location.pathname,
}
});
//<Link to={addVolumeLink}>New volume</Link>
const App = () => {
return (
<React.Fragment>
<ul>
<li>
<BaseLink to="/">Home</BaseLink>
</li>
<li>
<Link to={addVolumeLink}>New volume</Link>
</li>
</ul>
<Switch>
<Route path="/add-volume" render={() => <h1>Add Volume</h1>} />
<Route path="/" render={() => <h1>Home</h1>} />
</Switch>
</React.Fragment>
);
}
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<Router basename="/js">
<App />
</Router>
</React.StrictMode>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/5.3.4/react-router-dom.min.js" integrity="sha512-XJ25BbjmZQpaGkOJaDFXVJqfsBebv2aDLsBF3qT6zoC/gVj7XLuefNRzcgmlc5admYuODaYDrap8jzVyOsYFrw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="root" />