Skip to content

[Bug]: useSearchParams not giving correct value with useBlocker #12188

@abskrj

Description

@abskrj

What version of React Router are you using?

6.25.1

Steps to Reproduce

  1. create a Navigation Blocker Hook (which blocks query params change as well)
import { useEffect, useCallback } from 'react';
import { useLocation, useBeforeUnload, useBlocker } from 'react-router-dom';
import { validate as validateUUID } from 'uuid';

const useNavigationBlocker = (
    shouldBlock: boolean,
    checkSearchParams?: boolean,
) => {
    const location = useLocation();

    const blocker = useBlocker(
        useCallback(
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            (tx: any) => {
                if (!shouldBlock) return false;

                const nextLocation = tx.nextLocation;
                const currentPathname = location.pathname;
                const nextPathname = nextLocation.pathname;

                const currPathNameArr = currentPathname
                    .split('/')
                    .filter(Boolean);
                const nextPathNameArr = nextPathname.split('/').filter(Boolean);

                // Check for search params changes
                if (checkSearchParams) {
                    const currentSearchParams = new URLSearchParams(
                        location.search,
                    );
                    const nextSearchParams = new URLSearchParams(
                        nextLocation.search,
                    );

                    const searchParamsChanged =
                        Array.from(currentSearchParams.entries()).some(
                            ([key, value]) =>
                                nextSearchParams.get(key) !== value,
                        ) ||
                        Array.from(nextSearchParams.entries()).some(
                            ([key, value]) =>
                                currentSearchParams.get(key) !== value,
                        );

                    if (searchParamsChanged) {
                        return true; // Block navigation if search params have changed
                    }
                }

                // Allow navigation if saving a new item and redirecting to the item details page
                if (
                    currPathNameArr.length === 2 &&
                    nextPathNameArr.length === 2 &&
                    currPathNameArr[0] === nextPathNameArr[0] &&
                    currPathNameArr[1] === 'new' &&
                    validateUUID(nextPathNameArr[1])
                ) {
                    return false;
                }

                // Allow navigation if only search params are different
                if (currentPathname === nextPathname) {
                    return false;
                }

                return true;
            },
            [shouldBlock, location, checkSearchParams],
        ),
    );

    useBeforeUnload(
        useCallback(
            (event) => {
                if (shouldBlock) {
                    event.preventDefault();
                    event.returnValue = '';
                }
            },
            [shouldBlock],
        ),
    );

    useEffect(() => {
        if (blocker.state === 'blocked') {
            const proceed = window.confirm(
                'You have unsaved changes. Are you sure you want to leave?',
            );
            if (proceed) {
                setTimeout(blocker.proceed, 0);
            } else {
                blocker.reset();
            }
        }
    }, [blocker]);

    return blocker.state === 'blocked';
};
export default useNavigationBlocker;
  1. use this Hook in your Component
  2. fetch value of query using useSearchParam
  3. update the query params on some button click
  4. cancel the navigation change confirmation
  5. you will get the new value from the query param

Expected Behavior

after cancelling the navigation prompt you are blocking the navigation, though it doesn't reflect on the query in url, it updates in the state (searchParam)

Actual Behavior

it should be returning the old value in searchParam, after navigation is blocked

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions