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
  • Mitten in einem Formular ist
  • Mitten in einer Zahlung ist

In diesen Situationen sollte dem Benutzer eine Eingabeaufforderung oder eine benutzerdefinierte UI angezeigt werden, um zu bestätigen, dass er wegnavigieren 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 "Blocker"-Schichten zur gesamten zugrunde liegenden History API hinzu. Wenn Blocker 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 dem Benutzer eine beliebige UI anzuzeigen, um die Aktion zu bestätigen. Die blocker-Funktion jedes Blockers wird asynchron und sequenziell ausgeführt. Wenn eine Blocker-Funktion aufgelöst wird oder true zurückgibt, wird die Navigation zugelassen und alle anderen Blocker fahren damit fort, bis alle Blocker fortfahren dürfen. Wenn ein einzelner Blocker aufgelöst wird oder false zurückgibt, wird die Navigation abgebrochen und die restlichen blocker-Funktionen werden ignoriert.
  • Das onbeforeunload-Ereignis
    • Für Seitenereignisse, die wir nicht direkt kontrollieren können, verlassen wir uns auf das onbeforeunload-Ereignis des Browsers. Wenn der Benutzer versucht, den Tab oder das Fenster zu schließen, zu aktualisieren oder Seiteninhalte auf irgendeine Weise zu "entladen", wird der generische Dialog des Browsers "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 wie sie ist.

Wie benutze ich Navigation Blocking?

Es gibt 2 Möglichkeiten, Navigation Blocking zu verwenden

  • Hook/Logik-basiertes Blocking
  • Komponenten-basiertes Blocking

Hook/Logik-basiertes Blocking

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

tsx
import { useBlocker } from '@tanstack/solid-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/solid-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-Ort

tsx
import { useBlocker } from '@tanstack/solid-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/solid-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 selbst wenn shouldBlockFn false zurückgibt, das beforeunload-Ereignis des Browsers möglicherweise immer noch bei Seitenaktualisierungen oder dem Schließen von Tabs ausgelöst wird. Um dies zu steuern, können Sie die Option enableBeforeUnload verwenden, um den beforeunload-Handler bedingt zu registrieren

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

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

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

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

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

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

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

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

  // ...
}
tsx
import { Block } from '@tanstack/solid-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/solid-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>
  )
}
tsx
import { Block } from '@tanstack/solid-router'

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

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

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

  // OR

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

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

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

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

  // OR

  return (
    <Block shouldBlockFn={() => !formIsDirty} withResolver>
      {({ status, proceed, reset }) => <>{/* ... */}</>}
    </Block>
  )
}
tsx
import { useBlocker } from '@tanstack/solid-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/solid-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>
      )}
    </>
}
tsx
import { useBlocker } from '@tanstack/solid-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = createSignal(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/solid-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = createSignal(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>
      )}
    </>
}
tsx
import { useBlocker } from '@tanstack/solid-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/solid-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
    },
  })

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

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = createSignal(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/solid-router'

function MyComponent() {
  const [formIsDirty, setFormIsDirty] = createSignal(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
    },
  })

  // ...
}
tsx
import { Block } from '@tanstack/solid-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/solid-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.