Search code examples
reactjsreact-router-dom

'Navigate' refers to a value, but is being used as a type here


I am trying to implement a protected route by checking if the user is signed in. I am using React 18, Typescript, and React Router v6. However, it keeps on complaining 'Navigate' refers to a value, but is being used as a type here. Did you mean 'typeof Navigate'?ts(2749)

import { Navigate } from "react-router-dom";

interface Props {
   children?: ReactNode;
}

const Protected = ({ children }: Props) => {
    if (isSignedIn) {
        return <Navigate to="/" />;
    }
    return children;
};
export default Protected;

Solution

  • It seems like there's some issue with your node_modules. You may want to remove the folder entirely then re-run npm ci or yarn install --frozen-lockfile. You can also remove those lock files and just do a yarn install or npm install from scratch.

    Consider this barebones approach instead with a working sandbox example - flip const isSignedIn = true; and test the /secret path.

    package.json

    {
      "name": "react",
      "version": "1.0.0",
      "description": "React example starter project",
      "keywords": [
        "react",
        "starter"
      ],
      "dependencies": {
        "@types/jest": "^29.4.0",
        "@types/node": "^18.13.0",
        "@types/react": "^18.0.28",
        "@types/react-dom": "^18.0.10",
        "react": "18.2.0",
        "react-dom": "18.2.0",
        "react-router-dom": "^6.8.1",
        "react-scripts": "^5.0.1",
        "typescript": "^4.9.5"
      },
      "scripts": {
        "start": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test --env=jsdom",
        "eject": "react-scripts eject"
      },
      "browserslist": [
        ">0.2%",
        "not dead",
        "not ie <= 11",
        "not op_mini all"
      ]
    }
    

    tsconfig.json

    {
      "compilerOptions": {
        "target": "ESNext",
        "useDefineForClassFields": true,
        "lib": ["DOM", "DOM.Iterable", "ESNext"],
        "allowJs": false,
        "skipLibCheck": true,
        "esModuleInterop": false,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "module": "ESNext",
        "moduleResolution": "Node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "react-jsx",
        "baseUrl": "."
      },
      "include": ["src"]
    }
    

    index.tsx

    import { StrictMode } from "react";
    import { createRoot } from "react-dom/client";
    import { BrowserRouter } from "react-router-dom";
    
    import App from "./App";
    
    const rootElement = document.getElementById("root");
    const root = createRoot(rootElement as HTMLElement);
    
    root.render(
      <StrictMode>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </StrictMode>
    );
    

    App.tsx

    import { Route, Routes as RouteSwitch } from "react-router-dom";
    import Protected from "./Protected";
    
    const PublicComponent = () => <span>hello world</span>;
    const SecretComponent = () => <span>hello darkness my old friend</span>;
    
    export default function App() {
      return (
        <div>
          <RouteSwitch>
            <Route path="/" element={<PublicComponent />} />
            <Route
              path="/secret"
              element={
                <Protected>
                  <SecretComponent />
                </Protected>
              }
            />
          </RouteSwitch>
        </div>
      );
    }
    

    Protected.tsx

    import { FC, ReactNode, useEffect } from "react";
    import { useNavigate } from "react-router-dom";
    
    interface Props {
      children?: ReactNode;
    }
    
    const isSignedIn = true;
    
    const Protected: FC<{ children: ReactNode }> = ({ children }: Props) => {
      const navigate = useNavigate();
    
      useEffect(() => {
        if (!isSignedIn) navigate("/");
      }, [isSignedIn]);
    
      return <>{children}</>;
    };
    
    export default Protected;