useParams hook returns undefined in react functional component
Asked Answered
S

5

8

The app displays all photos <Photo> in a grid <PhotoGrid>, then once clicked, a function in <Photo> changes URL with history.push, and Router renders <Single> based on URL using useParams hook.

PhotoGrid -> Photo (changes URL onClick) -> Single based on URL (useParams). I must have messed something up, becouse useParams returns undefined.

Thanks for all ideas in advanced. App.js

class App extends Component {
  render() {
    return (
      <>
        <Switch>
          <Route exact path="/" component={PhotoGrid}/>
          <Route path="/view/:postId" component={Single}/>
        </Switch>
      </>
    )
  }
}
export default App;

Photogrid.js

export default function PhotoGrid() {
    const posts = useSelector(selectPosts);

    return (
        <div>
            hi
            {/* {console.log(posts)} */}
            {posts.map((post, i) => <Photo key={i} i={i} post={post} />)}
        </div>
    )
}

in Photo I change URL with history.push

const selectPost = () => {
  (...)
  history.push(`/view/${post.code}`);
  };

Single.js

import { useParams } from "react-router-dom";
export default function Single() {
    let { id } = useParams();
    console.log("id:",  id)   //returns undefined

    return (
      <div className="single-photo">
       the id is: {id} //renders nothing
      </div>
    )
}
Snick answered 6/3, 2021 at 14:44 Comment(0)
B
25

When using useParams, you have to match the destructure let { postId } = useParams(); to your path "/view/:postId".

Working Single.js

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

export default function Single() {
    const { postId } = useParams();
    console.log("this.context:",  postId )

    return (
      <div className="single-photo">
        {/* render something based on postId */}
      </div>
    )
}
Bulbiferous answered 6/3, 2021 at 14:50 Comment(3)
You should definitely use const instead of let. Destructure the param value -> const { postId } = useParams()Vitriolize
I'm saddened that I had to end up here to work that out given that I have already done it a hundred time in the code already. But thanks anyway.Ingratiate
thanks!! i had the same issue and solved it completelyFetor
B
4

You should use the same destructure as mentioned in your Route path. In this case, you should have written :

let { postID } = useParams();

I will mention two more mistakes which someone could make and face the same problem:

  1. You might use Router component in place of Route component.
  2. You might forget to mention the parameter in the path attribute of the Route component, while you would have mentioned it in the Link to component.
Burtis answered 26/9, 2021 at 4:53 Comment(0)
A
2

Ensure the component where you call useParams() is really a child from <Route>

Beware of ReactDOM.createPortal

const App = () => {
    return (
      <>
        <Switch>
          <Route exact path="/" component={PhotoGrid}/>
          <Route path="/view/:postId" component={Single}/>
        </Switch>
       <ComponentCreateWithPortal /> // Impossible to call it there
      </>
    )
}
Antipodes answered 1/8, 2022 at 14:56 Comment(0)
I
0

Not sure how helpful this is, but I had this exact issue but with a false positive.

The core issue is using too generic of a variable name in "id" in two locations.

The variable name "id" was both the parameter and obviously the id field on the data object returned from a fetch.

This is a perfect storm issue, but what happened in my specific use case is that the error was listing the line number of the undefined "id" in the useEffect call that used the parameter to fetch the data.

Let me be very clear, the error was not exactly stating the issue was with useParam (which in my experience is unreliable to use). But since it was exactly on a line of code calling "id" I went with it.

In the render method, I was listing the data object's "id" field. THIS is where the issue was popping an undefined since the component was rendering before the fetch came back, which is very expected behavior. I had just started putting the component together, so I had neglected to check the data object for existence prior to calling the id field.

Two prevention techniques a person could choose to use:

  1. Always name parameters, especially Id fields, to also include the name of the object itself. eg. vehicleId. If you then use plain "id" in your database, you have variable differentiation. If you also use vehicleId in the database, then you could further differentiate by naming the param "paramId" or some such. Naming database id fields is itself a book chapter so I leave that for another time.
  2. Don't write bad code like I did. Never reference a field on an object without checking null. I "never do that" as a policy myself, but we all make mistakes and I made a mistake.
Injustice answered 17/7, 2024 at 16:48 Comment(0)
I
-1

You have to check API that you are using. Sometimes it's called not just id. That's why useParams() do not see it

Inhambane answered 21/12, 2022 at 9:59 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Avivah

© 2022 - 2025 — McMap. All rights reserved.