Skip to content

Commit

Permalink
Enqueue premature calls to useNavigate() in react-router-6 adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
afn committed May 12, 2023
1 parent b14c97e commit b16b818
Showing 1 changed file with 45 additions and 2 deletions.
47 changes: 45 additions & 2 deletions packages/use-query-params-adapter-react-router-6/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext } from 'react';
import { useCallback, useContext, useEffect, useRef } from 'react';
import {
UNSAFE_NavigationContext,
useNavigate,
Expand All @@ -10,6 +10,48 @@ import {
QueryParamAdapterComponent,
} from 'use-query-params';

// Prevent premature calls to useNavigate() from failing with "You should call
// navigate() in a React.useEffect(), not when your component is first
// rendered".
//
// This warning happens even when navigate() is called from a useEffect(),
// because that useEffect is called before useNavigate's internal useEffect
// (parents' effects are invoked after children's effects).
//
// To get around this, enqueue premature useNavigate() calls, and process the
// queue in a useEffect() (which is called *after* useNavigate's effect).
//
// See: https://github.com/pbeshai/use-query-params/issues/211
function useNavigateDeferred() {
const realNavigate = useNavigate();
const navigateState = useRef({ isValid: false, navigate: realNavigate, queue: [] });

if(navigateState.current.navigate !== realNavigate) {
navigateState.current.isValid = false
}

const navigate = useCallback((to: any, options: any) => {
if(navigateState.current.isValid) {
navigateState.current.navigate(to, options);
}
else {
navigateState.current.queue.push([to, options]);
}
}, [])

useEffect(() => {
navigateState.current.isValid = true
navigateState.current.navigate = realNavigate

while(navigateState.current.queue.length > 0) {
const [to, options] = navigateState.current.queue.shift()
navigateState.current.navigate(to, options)
}
}, [realNavigate])

return navigate
}

/**
* Query Param Adapter for react-router v6
*/
Expand All @@ -22,10 +64,11 @@ export const ReactRouter6Adapter: QueryParamAdapterComponent = ({
// useLocation() output in case of some kind of breaking change we miss.
// see: https://github.com/remix-run/react-router/blob/f3d87dcc91fbd6fd646064b88b4be52c15114603/packages/react-router-dom/index.tsx#L113-L131
const { navigator } = useContext(UNSAFE_NavigationContext);
const navigate = useNavigate();
const navigate = useNavigateDeferred()
const router = useContext(UNSAFE_DataRouterContext)?.router;
const location = useLocation();


const adapter: QueryParamAdapter = {
replace(location) {
navigate(location.search || '?', {
Expand Down

0 comments on commit b16b818

Please sign in to comment.