javascriptreactjsreact-routerreact-router-dom

useLocation Hook Error: "useLocation() may be used only in the context of a <Router> component" in React Application


I'm developing a React application and I'm encountering an error when using the useLocation hook from the react-router-dom library. The error message I receive is:

Error: useLocation() may be used only in the context of a component.

Here is the relevant part of my App.js file:

import { useEffect, useState } from 'react'
import { createBrowserRouter, RouterProvider, useLocation } from 'react-router-dom'

// Components
import Navigation from './components/Navigation'
import Section from './components/Section'
import Product from './components/Product'
import Login from './components/Login'

// ABIs
import Dappazon from './abis/Dappazon.json'

// Config
import config from './config.json'

const ethers = require('ethers')

function App() {
  const [provider, setProvider] = useState(null)
  const [dappazon, setDappazon] = useState(null)

  const [account, setAccount] = useState(null)

  const [electronics, setElectronics] = useState(null)
  const [clothing, setClothing] = useState(null)
  const [toys, setToys] = useState(null)

  const [item, setItem] = useState(null)
  const [toggle, setToggle] = useState(false)

  const location = useLocation()

  const togglePop = (item) => {
    setItem(item)
    toggle ? setToggle(false) : setToggle(true)
  }

  const loadBlockchainData = async () => {
    const provider = new ethers.BrowserProvider(window.ethereum)
    setProvider(provider)
    const network = await provider.getNetwork()

    const dappazon = new ethers.Contract(config[network.chainId].dappazon.address, Dappazon, provider)
    setDappazon(dappazon)

    const items = []

    for (var i = 0; i < 9; i++) {
      const item = await dappazon.items(i + 1)
      items.push(item)
    }

    const electronics = items.filter((item) => item.category === 'electronics')
    const clothing = items.filter((item) => item.category === 'clothing')
    const toys = items.filter((item) => item.category === 'toys')

    setElectronics(electronics)
    setClothing(clothing)
    setToys(toys)
  }

  useEffect(() => {
    loadBlockchainData()
  }, [])

  const router = createBrowserRouter([
    {
      path: "/login",
      element: <><Navigation account={account} setAccount={setAccount} /><Login /></>
    },
    {
      path: "/",
      element: <><Navigation account={account} setAccount={setAccount} /></>
    },
    {
      path: "/Clothing&Jewelry",
      element: <><Navigation account={account} setAccount={setAccount} /><Section title={"Clothing & Jewelry"} items={clothing} togglePop={togglePop} /></>
    },
    {
      path: "/Electronics&Gadgets",
      element: <><Navigation account={account} setAccount={setAccount} /><Section title={"Electronics & Gadgets"} items={electronics} togglePop={togglePop} /></>
    },
    {
      path: "/Toys&Gaming",
      element: <><Navigation account={account} setAccount={setAccount} /><Section title={"Toys & Gaming"} items={toys} togglePop={togglePop} /></>
    },
  ])

  return (
    <>
      <div>
        {/* <Navigation account={account} setAccount={setAccount} /> */}
        <RouterProvider router={router} />

        {electronics && clothing && toys && location.pathname === '/' && (
          <>
            <h2>Dappazon Best Sellers</h2>
            <Section title={"Clothing & Jewelry"} items={clothing} togglePop={togglePop} />
            <Section title={"Electronics & Gadgets"} items={electronics} togglePop={togglePop} />
            <Section title={"Toys & Gaming"} items={toys} togglePop={togglePop} />
          </>
        )}

        {toggle && (
          <Product item={item} provider={provider} account={account} dappazon={dappazon} togglePop={togglePop} />
        )}
      </div>
    </>
  );
}

export default App;

I have already ensured that RouterProvider wraps the component that uses useLocation, but the error persists. The useLocation hook is being used within the App component which is wrapped inside the RouterProvider as shown above.

What could be causing this issue and how can I resolve it?


Solution

  • In your App component, useLocation is being called directly within the App function. However, RouterProvider is supposed to wrap the entire component tree where the useLocation hook is used. To fix the error, move the useLocation logic into a separate component, then wrap the component tree inside RouterProvider:

    import { useEffect, useState } from 'react'
    import { createBrowserRouter, RouterProvider, useLocation } from 'react-router-dom'
    import Navigation from './components/Navigation'
    import Section from './components/Section'
    import Product from './components/Product'
    import Login from './components/Login'
    import Dappazon from './abis/Dappazon.json'
    import config from './config.json'
    const ethers = require('ethers')
    
    const MainContent = ({ togglePop, provider, dappazon, account, electronics, clothing, toys, item, toggle }) => {
      const location = useLocation()
      return (
        <>
          {electronics && clothing && toys && location.pathname === '/' && (
            <>
              <h2>Dappazon Best Sellers</h2>
              <Section title={"Clothing & Jewelry"} items={clothing} togglePop={togglePop} />
              <Section title={"Electronics & Gadgets"} items={electronics} togglePop={togglePop} />
              <Section title={"Toys & Gaming"} items={toys} togglePop={togglePop} />
            </>
          )}
          {toggle && (
            <Product item={item} provider={provider} account={account} dappazon={dappazon} togglePop={togglePop} />
          )}
        </>
      )
    }
    
    function App() {
      const [provider, setProvider] = useState(null)
      const [dappazon, setDappazon] = useState(null)
      const [account, setAccount] = useState(null)
      const [electronics, setElectronics] = useState(null)
      const [clothing, setClothing] = useState(null)
      const [toys, setToys] = useState(null)
      const [item, setItem] = useState(null)
      const [toggle, setToggle] = useState(false)
    
      const togglePop = (item) => {
        setItem(item)
        setToggle(!toggle)
      }
    
      const loadBlockchainData = async () => {
        const provider = new ethers.BrowserProvider(window.ethereum)
        setProvider(provider)
        const network = await provider.getNetwork()
        const dappazon = new ethers.Contract(config[network.chainId].dappazon.address, Dappazon, provider)
        setDappazon(dappazon)
        const items = []
        for (var i = 0; i < 9; i++) {
          const item = await dappazon.items(i + 1)
          items.push(item)
        }
        setElectronics(items.filter(item => item.category === 'electronics'))
        setClothing(items.filter(item => item.category === 'clothing'))
        setToys(items.filter(item => item.category === 'toys'))
      }
    
      useEffect(() => {
        loadBlockchainData()
      }, [])
    
      const router = createBrowserRouter([
        { path: "/login", element: <><Navigation account={account} setAccount={setAccount} /><Login /></> },
        { path: "/", element: <><Navigation account={account} setAccount={setAccount} /></> },
        { path: "/Clothing&Jewelry", element: <><Navigation account={account} setAccount={setAccount} /><Section title={"Clothing & Jewelry"} items={clothing} togglePop={togglePop} /></> },
        { path: "/Electronics&Gadgets", element: <><Navigation account={account} setAccount={setAccount} /><Section title={"Electronics & Gadgets"} items={electronics} togglePop={togglePop} /></> },
        { path: "/Toys&Gaming", element: <><Navigation account={account} setAccount={setAccount} /><Section title={"Toys & Gaming"} items={toys} togglePop={togglePop} /></> },
      ])
    
      return (
        <RouterProvider router={router}>
          <MainContent
            togglePop={togglePop}
            provider={provider}
            dappazon={dappazon}
            account={account}
            electronics={electronics}
            clothing={clothing}
            toys={toys}
            item={item}
            toggle={toggle}
          />
        </RouterProvider>
      )
    }
    
    export default App