Představte si, že po vyplnění povinného a nudného dotazníku omylem zavřete kartu prohlížeče. Všechny vaše odpovědi jsou nyní ztraceny.
Frustrující, že?
Takový zážitek byste svým uživatelům nechtěli poskytnout. Zde je návod, jak to můžete napravit.
Problém:
Jak upozornit uživatele, když náhodou...
- Znovu načtěte stránku.
- Zavřete kartu nebo okno prohlížeče.
- Stiskněte tlačítko Zpět v prohlížeči.
- Klikněte na odkaz/změňte trasu.
Řešení:
Část 1. Detekce opětovného načtení stránky a zavření karty prohlížeče
Zavření karty/okna nebo událost opětovného načtení stránky znamenají, že aktuální dokument a jeho zdroje budou odstraněny (uvolněny). V tomto případě beforeunload
událost je spuštěna.
V bodě, ve kterém beforeunload
událost je spuštěna, dokument je stále viditelný a událost lze zrušit, což znamená unload
události lze zabránit, jako by se nikdy nestala.
Tato událost umožňuje webové stránce spustit potvrzovací dialog s dotazem uživatele, zda skutečně chce stránku opustit. Pokud uživatel potvrdí, prohlížeč přejde na novou stránku, v opačném případě navigaci zruší.
Zabránění beforeunload
událost
window.onbeforeunload = (event) => {
const e = event || window.event;
// Cancel the event
e.preventDefault();
if (e) {
e.returnValue = ''; // Legacy method for cross browser support
}
return ''; // Legacy method for cross browser support
};
Všechny 3 metody výše e.preventDefault()
, e.returnValue = ''
a return ''
zabránit provedení události.
Příklad zobrazeného potvrzovacího pole:
Poznámka: Bohužel přizpůsobená zpráva není podporována ve všech prohlížečích
Zobrazit výzvu na základě stavu
#1 Vytvořte funkci se stavem React showExitPrompt
jako parametr a inicializujte onbeforeunload
posluchač uvnitř funkce. Použijte stav uvnitř posluchače událostí.
Proč předávat stav React jako parametr?
Protože onbeforeunload
je vanilkový javascriptový posluchač událostí a jakákoli změna stavu React neaktualizuje stav uvnitř jeho zpětného volání.
import { useState } from 'react';
const initBeforeUnLoad = (showExitPrompt) => {
window.onbeforeunload = (event) => {
// Show prompt based on state
if (showExitPrompt) {
const e = event || window.event;
e.preventDefault();
if (e) {
e.returnValue = ''
}
return '';
}
};
};
#2 Vytvořte stav showExitPrompt
ke správě výzvy a registraci posluchače událostí při načítání stránky.
function MyComponent() {
const [showExitPrompt, setShowExitPrompt] = useState(false);
// Initialize the beforeunload event listener after the resources are loaded
window.onload = function() {
initBeforeUnLoad(showExitPrompt);
};
}
#3 Znovu inicializujte posluchač události při změně stavu.
import { useState, useEffect } from 'react';
const initBeforeUnLoad = (showExitPrompt) => {
// … code
}
function MyComponent() {
const [showExitPrompt, setShowExitPrompt] = useState(false);
window.onload = function() {
initBeforeUnLoad(showExitPrompt);
};
// Re-Initialize the onbeforeunload event listener
useEffect(() => {
initBeforeUnLoad(showExitPrompt);
}, [showExitPrompt]);
}
Nyní jste připraveni jej použít uvnitř vaší komponenty. ALE je efektivní vytvořit vlastní hák pro nastavení a přístup ke stavu kdekoli v aplikaci.
Použijte vlastní háček
#1 Soubor háčku useExitPrompt.js
import { useState, useEffect } from 'react';
const initBeforeUnLoad = (showExitPrompt) => {
window.onbeforeunload = (event) => {
if (showExitPrompt) {
const e = event || window.event;
e.preventDefault();
if (e) {
e.returnValue = '';
}
return '';
}
};
};
// Hook
export default function useExitPrompt(bool) {
const [showExitPrompt, setShowExitPrompt] = useState(bool);
window.onload = function() {
initBeforeUnLoad(showExitPrompt);
};
useEffect(() => {
initBeforeUnLoad(showExitPrompt);
}, [showExitPrompt]);
return [showExitPrompt, setShowExitPrompt];
}
#2 Komponentní soubor MyComponent.js
Poznámka: Budete muset resetovat hodnotu showExitPrompt
stav na výchozí, když je komponenta odpojena.
import useExitPrompt from './useExitPrompt.js'
export default function MyComponent() {
const [showExitPrompt, setShowExitPrompt] = useExitPrompt(false);
const handleClick = (e) => {
e.preventDefault();
setShowExitPrompt(!showExitPrompt)
}
//NOTE: this similar to componentWillUnmount()
useEffect(() => {
return () => {
setShowExitPrompt(false)
}
}, [])
return (
<div className="App">
<form>{/*Your code*/}</form>
<button onClick={handleClick}>Show/Hide the prompt</button>
<Child setShowExitPrompt={setShowExitPrompt} />
</div>
);
}
NEBO
#2 Komponentní soubor App.js
Předejte to svým podřízeným komponentám prostřednictvím Context.Provider
a k hodnotě přistupujte pomocí useContext()
háček kdekoli ve vaší aplikaci.
import useExitPrompt from './useExitPrompt.js'
import MyContext from './MyContext.js'
export default function App() {
const [showExitPrompt, setShowExitPrompt] = useExitPrompt(false);
return (
<div className="App">
<MyContext.Provider value={{showExitPrompt, setShowExitPrompt}}>
<MyMainApp />
</MyContext.Provider>
</div>
);
}
export default function MyComponent() {
const { showExitPrompt, setShowExitPrompt } = useContext(MyContext);
//NOTE: this works similar to componentWillUnmount()
useEffect(() => {
return () => {
setShowExitPrompt(false);
}
}, [])
return (
<div>{/* your code */}</div>
);
}
Část 2. Detekce změny trasy/stránky a zpět v prohlížeči
Podobně jako u výše uvedených akcí, když uživatel klikne na odkaz, je přesměrován na novou stránku a dokument a jeho zdroje budou uvolněny.
React Router však funguje jinak, implementuje rozhraní History API, které poskytuje přístup k historii relace prohlížeče. Kliknutím na běžný odkaz přejdete na novou adresu URL a nový dokument (stránku), mezitím history
umožňuje „falešnou“ adresu URL, aniž byste opustili stránku.
location.pathname
vs history.pushState()
window.location.pathname = '/dummy-page'
V/S
window.history.pushState({}, '', '/dummy-page')
Vidíte ten rozdíl? history.pushState()
nezmění pouze URL nic jiného, celá stránka zůstane nedotčena, zatímco location.pathname
přesměruje vás na tuto novou stránku, pravděpodobně zobrazí chybu 404, protože taková trasa neexistuje.
Zobrazení výzvy s getUserConfirmation()
a <Prompt/>
komponent
React Router poskytuje podporu getUserConfirmation()
v <BrowserRouter>
pro potvrzení navigace a komponenty <Prompt/>
zobrazíte vlastní zprávu z vašich podřízených komponent.
#1 Kořenový soubor App.js
import { BrowserRouter } from 'react-router-dom';
function App() {
return (
<BrowserRouter getUserConfirmation={(message, callback) => {
// this is the default behavior
const allowTransition = window.confirm(message);
callback(allowTransition);
}}
>
<Routes />
</BrowserRouter>
);
}
window.confirm()
zobrazí zprávu, kterou předáte, v React Routeru <Prompt />
komponent z vašich příslušných dětských komponent. callback()
funkce vyžaduje booleovský parametr, který zabrání přechodu na novou stránku.
#2 Komponentní soubor MyForm.js
<Prompt />
má 2 rekvizity, when
a message
. Pokud when
hodnota prop je nastavena na true a uživatel klikne na jiný odkaz, zobrazí se mu zpráva předaná v message
rekvizity.
import { Prompt } from 'react-router-dom';
function MyForm() {
const [isFormIncomplete, setIsFormIncomplete] = useState(true);
return (
<div>
<form>{/*Your code*/}</form>
<Prompt
when={isFormIncomplete}
message="Are you sure you want to leave?" />
</div>
)
}
Příklad zobrazeného potvrzovacího pole:
Souhrn
Pokud akce uživatele...
- Odstraní zdroje stránky, použijte
beforeunload
událost vanilla JavaScript, která uživatele vyzve. - Změňte pouze zobrazení, použijte
getUserConfirmation()
v<BrowserRouter/>
spolu s<Prompt />
komponenta k zobrazení výzvy uživatele.