skip to Main Content

I have a Browser Context set up like this:

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BrowserRouter>
      <ErrorBoundary FallbackComponent={CustomError}>
        <App />
      </ErrorBoundary>
    </BrowserRouter>
  </React.StrictMode>,
)

and the app component is like this:

export const App = () => {
  if (isDevEnv()) {
    const navigate = useNavigate();
    
    const restoreOriginalUri = async (originalUri: string) => {
      navigate(toRelativeUrl(originalUri || '/', window.location.origin));
    };

    return (
      <Security restoreOriginalUri={restoreOriginalUri}>
        <AppRoutes />
      </Security>
    )
  }

  return (
    <AppRoutes />
  )
}

At this point everything works just fine, but eslint complains that react hook cannot be used conditionally.

I decided to make the code beautifully, so that eslint would not complain. For that I separated DEV logic into a subcomponent like this:

App.tsx become very short. All logic moved to a subcomponent:

export const App = () => {
  const children: React.JSX.Element = <AppRoutes />;
  return <MySecurityComponent children={children} />
}

the MySecurityComponent is like this:

type MySecurityComponentType = (props: { children: React.JSX.Element }) => React.JSX.Element;

export const MySecurityComponent: MySecurityComponentType = ({children}): React.JSX.Element => {
  if (isDevEnv()) {
    return <MySecurityComponentDev children={children} />
  }
  }
  return children;
}

And a MySecurityComponentDev (sub-sub component) looks like this:

type MySecurityComponentDevType = (props: { children: React.JSX.Element }) => React.JSX.Element;

const MySecurityComponentDev: MySecurityComponentDevType = ({ children }) => {
  const navigate = useNavigate();

  const restoreOriginalUri = async  originalUri: string) => {
    navigate(toRelativeUrl(originalUri || '/', window.location.origin));
  };

  return (
    <Security restoreOriginalUri={restoreOriginalUri}>
      {children}
    </Security>
  );
}

And now I see such an error:
useNavigate() may be used only in the context of a <Router> component.

Am I missing something to push the router context through the tree of components?
Or it is allowed to use router context only in a top-level children and inside routes?

2

Answers


  1. Chosen as BEST ANSWER

    I managed to solve the issue with react router context. The router context is accessible in the root component (App), but it is not accessible in the child components, which are not inside the Routes/Route. I tried to get the "useNavigation" in the App component and pass it through the children with props. And it worked out! And ESLint does not complain!!!

    export const App = () => {
      const navigate = useNavigate();
      const children: React.JSX.Element = <AppRoutes/>;
      return <SecurityComponent navigate={navigate} children={children}/>
    }
    
    type SecurityComponentType = (props: { children: React.JSX.Element, 
    navigate: NavigateFunction }) => React.JSX.Element;
    
    export const SecurityComponent: SecurityComponentType = ({children, 
    navigate}): React.JSX.Element => {
    ....
    }
    

  2. I think you’ve overcomplicated your code a bit. The most trivial solution is simply to call the useNavigate hook from the first code example from the correct place, e.g. the React component body. It will be in scope.

    export const App = () => {
      const navigate = useNavigate();
    
      if (isDevEnv()) {
        const restoreOriginalUri = (originalUri: string) => {
          navigate(toRelativeUrl(originalUri || '/', window.location.origin));
        };
    
        return (
          <Security restoreOriginalUri={restoreOriginalUri}>
            <AppRoutes />
          </Security>
        );
      }
    
      return  <AppRoutes />;
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search