React Router Link reloading page: conflitto con evento esterno
Sto usando il componente Link di React Router per gestire la transizione della pagina nella mia app.
La mia app è inclusa in una pagina web esterna, che ha alcuni script (ovvero Google Tag Manager) che definiscono eventi sui link a livello di documento.
Ciò causa un conflitto con gli eventi di collegamento: la pagina viene sempre ricaricata quando si fa clic su un collegamento.
Ho provato a interrompere la propagazione sul mio componente Link con un callback onClick, senza fortuna: <Link onClick={(e) => { e.stopPropagation(); }} [...]>...</Link>
Vedo che gli eventi di collegamento sono definiti anche a livello di documento , quindi non so come fermare gli eventi esterni.
Qualche indizio su questo?
Grazie !
Risposte
Ho affrontato un problema abbastanza simile su una delle mie app e penso che la soluzione sia evitare di usare react-router <Link /> componentse puoi.
Invece di utilizzare il <Link />componente, ho utilizzato l' withRouter()HOC fornito da react-router:
Cosa hai scritto :
Class MyComponent extends React.Component{
<Link onClick={(e) => { e.stopPropagation(); }} [...]>
...
</Link>
}
export default MyComponent;
Può essere sostituito da:
Class MyComponent extends React.Component{
navigate = (event, location) => {
event.preventDefault();
this.props.history.push(location);
}
<a onClick={(e, href) => { this.navigate(e, href) }} [...]>
...
</a>
}
export default withRouter(MyComponent);
Ciò dovrebbe impedire al collegamento di rilevare altri eventi a livello di documento e ottenere lo stesso risultato del <Link />componente.
Aggiornamento: sembra che non ci sia un modo semplice per farlo senza rendere l'intera app all'interno di un file iframe.
Se non ti dispiace farlo, è abbastanza facile (preso da Come impostare il contenuto dell'iframe di un componente di reazione ):
const Frame = ({ children }) => {
const [ref, setRef] = useState(null)
const mountNode = ref?.contentWindow.document.body
return (
<iframe title="iframe" ref={setRef}>
{mountNode && ReactDOM.createPortal(children, mountNode)}
</iframe>
)
}
Quindi, avvolgi la tua app principale come figlia del Framecomponente. I clic all'interno di una iframebolla non verranno visualizzati nella finestra principale, come descritto in dettaglio in Cattura clic sul div che circonda un iframe .
Ecco una demo di CodeSandbox che mostra questo approccio.
Risposta originale (non funzionante) di seguito
Sul tuo Appo Routercomponente:
const blockClicks = useCallback((e) => {
const { target } = e
const { nodeName, href } = target
if (
target.closest('#root-id') &&
nodeName === 'A' &&
href &&
(/^https?:\/\//.test(!href) ||
href.startsWith(window.location.origin)
)) {
// amend logic specific to your use case
// - we want to avoid blocking irrelevant clicks
e.stopPropagation()
}
})
useEffect(() => {
document.addEventListener('click', blockClicks, true)
return () =>
document.removeEventListener('click', blockClicks, true)
})