Framework
Version
Integrationen

Navigationsblockierung

Navigation Blocking ist eine Möglichkeit, die Navigation zu verhindern. Dies ist typisch, wenn ein Benutzer versucht zu navigieren, während er

  • Ungespeicherte Änderungen hat
  • Sich mitten in einem Formular befindet
  • Sich mitten in einer Zahlung befindet

In diesen Situationen sollte dem Benutzer eine Aufforderung oder eine benutzerdefinierte Benutzeroberfläche angezeigt werden, um zu bestätigen, dass er weg navigieren möchte.

  • Wenn der Benutzer bestätigt, wird die Navigation wie gewohnt fortgesetzt
  • Wenn der Benutzer abbricht, werden alle ausstehenden Navigationsvorgänge blockiert

Wie funktioniert Navigation Blocking?

Navigation Blocking fügt eine oder mehrere Schichten von "Blockern" zur gesamten zugrunde liegenden History API hinzu. Wenn Blockierer vorhanden sind, wird die Navigation über eine der folgenden Methoden pausiert:

  • Benutzerdefinierte Benutzeroberfläche
    • Wenn die Navigation durch etwas ausgelöst wird, das wir auf Router-Ebene kontrollieren, können wir Ihnen erlauben, jede Aufgabe auszuführen oder jede Benutzeroberfläche anzuzeigen, die Sie dem Benutzer zur Bestätigung der Aktion wünschen. Jede blocker-Funktion jedes Blockierers wird asynchron und sequenziell ausgeführt. Wenn eine blocker-Funktion sich auflöst oder true zurückgibt, wird die Navigation zugelassen und alle anderen Blocker fahren auf die gleiche Weise fort, bis alle Blocker fortfahren dürfen. Wenn sich eine einzelne Blocker-Funktion auflöst oder false zurückgibt, wird die Navigation abgebrochen und der Rest der blocker-Funktionen wird ignoriert.
  • Das Ereignis onbeforeunload
    • Für Seitenereignisse, die wir nicht direkt steuern können, verlassen wir uns auf das Browserereignis onbeforeunload. Wenn der Benutzer versucht, den Tab oder das Fenster zu schließen, die Seite zu aktualisieren oder die Seitenressourcen auf irgendeine Weise zu "entladen", wird der generische Browserdialog "Sind Sie sicher, dass Sie diese Seite verlassen möchten?" angezeigt. Wenn der Benutzer bestätigt, werden alle Blocker umgangen und die Seite wird entladen. Wenn der Benutzer abbricht, wird das Entladen abgebrochen und die Seite bleibt unverändert.

Wie verwende ich Navigation Blocking?

Es gibt 2 Möglichkeiten, Navigation Blocking zu verwenden

  • Hook/logikbasierte Blockierung
  • Komponentenbasierte Blockierung

Hook/logikbasierte Blockierung

Stellen wir uns vor, wir möchten die Navigation verhindern, wenn ein Formular schmutzig ist. Wir können dies tun, indem wir den Hook useBlocker verwenden.

tsx
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  useBlocker({
    shouldBlockFn: () => {
      if (!formIsDirty) return false

      const shouldLeave = confirm('Are you sure you want to leave?')
      return !shouldLeave
    },
  })

  // ...
}
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  useBlocker({
    shouldBlockFn: () => {
      if (!formIsDirty) return false

      const shouldLeave = confirm('Are you sure you want to leave?')
      return !shouldLeave
    },
  })

  // ...
}

shouldBlockFn gibt Ihnen typsicheren Zugriff auf den current und next Standort

tsx
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  // always block going from /foo to /bar/123?hello=world
  const { proceed, reset, status } = useBlocker({
    shouldBlockFn: ({ current, next }) => {
      return (
        current.routeId === '/foo' &&
        next.fullPath === '/bar/$id' &&
        next.params.id === 123 &&
        next.search.hello === 'world'
      )
    },
    withResolver: true,
  })

  // ...
}
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  // always block going from /foo to /bar/123?hello=world
  const { proceed, reset, status } = useBlocker({
    shouldBlockFn: ({ current, next }) => {
      return (
        current.routeId === '/foo' &&
        next.fullPath === '/bar/$id' &&
        next.params.id === 123 &&
        next.search.hello === 'world'
      )
    },
    withResolver: true,
  })

  // ...
}

Beachten Sie, dass auch wenn shouldBlockFn false zurückgibt, das Browserereignis beforeunload bei Seitenaktualisierungen oder dem Schließen von Tabs immer noch ausgelöst werden kann. Um dies zu kontrollieren, können Sie die Option enableBeforeUnload verwenden, um den beforeunload-Handler bedingt zu registrieren

tsx
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  useBlocker({
    {/* ... */}
    enableBeforeUnload: formIsDirty, // or () => formIsDirty
  })

  // ...
}
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  useBlocker({
    {/* ... */}
    enableBeforeUnload: formIsDirty, // or () => formIsDirty
  })

  // ...
}

Weitere Informationen zum Hook useBlocker finden Sie in der API-Referenz.

Komponentenbasierte Blockierung

Neben der logischen/hook-basierten Blockierung können Sie die Komponente Block verwenden, um ähnliche Ergebnisse zu erzielen.

tsx
import { Block } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  return (
    <Block
      shouldBlockFn={() => {
        if (!formIsDirty) return false

        const shouldLeave = confirm('Are you sure you want to leave?')
        return !shouldLeave
      }}
      enableBeforeUnload={formIsDirty}
    />
  )

  // OR

  return (
    <Block
      shouldBlockFn={() => formIsDirty}
      enableBeforeUnload={formIsDirty}
      withResolver
    >
      {({ status, proceed, reset }) => <>{/* ... */}</>}
    </Block>
  )
}
import { Block } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  return (
    <Block
      shouldBlockFn={() => {
        if (!formIsDirty) return false

        const shouldLeave = confirm('Are you sure you want to leave?')
        return !shouldLeave
      }}
      enableBeforeUnload={formIsDirty}
    />
  )

  // OR

  return (
    <Block
      shouldBlockFn={() => formIsDirty}
      enableBeforeUnload={formIsDirty}
      withResolver
    >
      {({ status, proceed, reset }) => <>{/* ... */}</>}
    </Block>
  )
}

Wie kann ich eine benutzerdefinierte Benutzeroberfläche anzeigen?

In den meisten Fällen ist die Verwendung von window.confirm in der Funktion shouldBlockFn mit withResolver: false im Hook ausreichend, da dies dem Benutzer klar anzeigt, dass die Navigation blockiert wird, und die Blockierung basierend auf seiner Antwort auflöst.

In einigen Situationen möchten Sie jedoch möglicherweise eine benutzerdefinierte Benutzeroberfläche anzeigen, die absichtlich weniger störend und besser in das Design Ihrer App integriert ist.

Hinweis: Der Rückgabewert von shouldBlockFn löst die Blockierung nicht auf, wenn withResolver true ist.

Hook/logikbasierte benutzerdefinierte UI mit Resolver

tsx
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  const { proceed, reset, status } = useBlocker({
    shouldBlockFn: () => formIsDirty,
    withResolver: true,
  })

  // ...

  return (
    <>
      {/* ... */}
      {status === 'blocked' && (
        <div>
          <p>Are you sure you want to leave?</p>
          <button onClick={proceed}>Yes</button>
          <button onClick={reset}>No</button>
        </div>
      )}
    </>
}
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  const { proceed, reset, status } = useBlocker({
    shouldBlockFn: () => formIsDirty,
    withResolver: true,
  })

  // ...

  return (
    <>
      {/* ... */}
      {status === 'blocked' && (
        <div>
          <p>Are you sure you want to leave?</p>
          <button onClick={proceed}>Yes</button>
          <button onClick={reset}>No</button>
        </div>
      )}
    </>
}

Hook/logikbasierte benutzerdefinierte UI ohne Resolver

tsx
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  useBlocker({
    shouldBlockFn: () => {
      if (!formIsDirty) {
        return false
      }

      const shouldBlock = new Promise<boolean>((resolve) => {
        // Using a modal manager of your choice
        modals.open({
          title: 'Are you sure you want to leave?',
          children: (
            <SaveBlocker
              confirm={() => {
                modals.closeAll()
                resolve(false)
              }}
              reject={() => {
                modals.closeAll()
                resolve(true)
              }}
            />
          ),
          onClose: () => resolve(true),
        })
      })
      return shouldBlock
    },
  })

  // ...
}
import { useBlocker } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  useBlocker({
    shouldBlockFn: () => {
      if (!formIsDirty) {
        return false
      }

      const shouldBlock = new Promise<boolean>((resolve) => {
        // Using a modal manager of your choice
        modals.open({
          title: 'Are you sure you want to leave?',
          children: (
            <SaveBlocker
              confirm={() => {
                modals.closeAll()
                resolve(false)
              }}
              reject={() => {
                modals.closeAll()
                resolve(true)
              }}
            />
          ),
          onClose: () => resolve(true),
        })
      })
      return shouldBlock
    },
  })

  // ...
}

Komponentenbasierte benutzerdefinierte UI

Ähnlich wie beim Hook gibt die Komponente Block die gleichen Status und Funktionen wie Render Props zurück.

tsx
import { Block } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  return (
    <Block shouldBlockFn={() => formIsDirty} withResolver>
      {({ status, proceed, reset }) => (
        <>
          {/* ... */}
          {status === 'blocked' && (
            <div>
              <p>Are you sure you want to leave?</p>
              <button onClick={proceed}>Yes</button>
              <button onClick={reset}>No</button>
            </div>
          )}
        </>
      )}
    </Block>
  )
}
import { Block } from '@tanstack/react-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = useState(false)

  return (
    <Block shouldBlockFn={() => formIsDirty} withResolver>
      {({ status, proceed, reset }) => (
        <>
          {/* ... */}
          {status === 'blocked' && (
            <div>
              <p>Are you sure you want to leave?</p>
              <button onClick={proceed}>Yes</button>
              <button onClick={reset}>No</button>
            </div>
          )}
        </>
      )}
    </Block>
  )
}
Unsere Partner
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.