0

I'm working on a personal project and i have now been stuck with a react router problem since a couple of day.

I'am fetching an API and trying to render a route with a single id so when i click on the card link everything is working fine but as soon as a try to render the link manually on a new tab for example i got a console error

TypeError: Cannot read property 'position_name' of undefined

 | const Articles = ({ location }) => (
  5 |   <ArticleComponent
> 6 |     position_name={location.state.position_name}
  7 |     workplace_name={location.state.workplace_name}

Here are my routes on the APP.js file

    <Switch>
      <Route exact path="/" component={JobCard} />
      <Route path="/jobs/:_id" component={Articles} />
    </Switch>

and the component where i fetch the card.

  <div key={job._id} className="blog-card">
                    <div className="description">
                      <Link
                        className="link-apply"
                        to={{
                          pathname: `/jobs/${job._id}`,
                          state: job
                        }}
                      >
                        <h5 className="position-name">{job.position_name}</h5>
                        <p className="place">{job.workplace_name}</p>
                        <p className="location-job">{job.location}</p>
                      </Link>
                    </div>

So what i'm trying to achieve is to be able to render the articles when i type the link manually, for example: http://localhost:3000/jobs/5ccc770c6ac54350001f0954

But it's only rendering when i click on the card for some reasons.

Thanks in advance for the help

Ron
  • 3
  • 2
  • I don't use React Router, but I've a feeling state is maybe using History state to store this, this won't be valid for new windows. Try using query params instead. – Keith Jul 12 '19 at 11:42
  • This seems there is a problem with your .htaccess – Mosè Raguzzini Jul 12 '19 at 12:25
  • I think it's because i cant refresh state, but i don't know how to solve this problem in any other way, it's working when i'm fetching a single id in postman – Ron Jul 12 '19 at 12:44
  • When calling your page from click on your card, you set state inside your `` component : `state: job`. But when typing your url, you aren’t sending the required additional information (your `state` key), so your component can’t read it (it is, in fact, undefined). If you need to send information by typing an url, said information must be contained in, or accessible by URL parameter. So your `position_name` and `workplace_name` must either be in your URL, along with `job_id`, or `job_id` must allow you to do a sort of database query to fetch those information. – KawaLo Jul 12 '19 at 12:57
  • Do you have an idea how i can achieve that because i'm quiet lost right now, I can access element by single id from my database – Ron Jul 12 '19 at 13:08

1 Answers1

0

But it's only rendering when i click on the card for some reasons.

When you click on the <Link> in your card, you can see your request looks like following :

<Link
  className="link-apply"
  to={{
    pathname: `/jobs/${job._id}`,
    state: job
  }}
>

When doing this, your request contain 2 information :

  1. It wants to go to the path pathname
  2. It sends along a state, containing your job values

So in your component

const Articles = ({ location }) => (
  <ArticleComponent
    position_name={location.state.position_name}
    workplace_name={location.state.workplace_name}
    . . .
  />
);

location object looks like this :

location = {
  pathname: ‘your_url’,
  state: {
    position_name: ‘your_string’,
    workplace_name: ‘your_string’
    . . .
  }
}

And that’s why calling location.state.position_name works. However, when you type your URL manually, location will only look like this :

location = {
  pathname: ‘your_url’
}

Because, you actually didn’t passed any state ! It is impossible to achieve this, you cannot add anything to your location props by typing your URL. So, let’s do the work.

Solution

You need another way to get your state information. As you can retrieve anything from your database, the following should do. Just change your <Article> component :

Article.js

// You can pass the whole props or destructure the ones you need
// like you did before. In my example, I don’t nee anything so I
// just leave it blank
const Articles = () => {
  // Not the best method but it works everywhere, since I don’t know
  // your whole project structure. Read below for more information.
  const pathname = window.location.pathname.split(‘/‘);
  const job_id = pathname[pathname.length - 1];

  // Make your query here
  // Assume query() is a function that return a job with given id
  // from your database
  const job = query(job_id);
  let position_name;
  let workplace_name;

  // If query was successful
  if (job) {
    position_name = job.position_name;
    workplace_name = job.workplace_name;
  }

  return (
    <ArticleComponent
      position_name={position_name}
      workplace_name={workplace_name}
      . . .
    />
  );
};

Thus, you retrieve needed information without anything more than your job_id, and you allow users to query from a direct URL.

Note about pathname

I retrieved job_id from window.location, which is a default javascript URL parser. However, it’s kinda ugly, not to mention it isn’t quite scalable.

According to how your project tree is structured, if your element is attached to a router, you can use query_string library, as mentioned in this topic (be aware of some methods that doesn’t work anymore in recent React version).

Since your user is potential to errors (like, for example, entering an id that doesn’t exist), you need to deal with errors in your query, for example by returning null values, or redirect to a 404 if query didn’t succeed.

KawaLo
  • 1,783
  • 3
  • 16
  • 34
  • Thank you for your well documented answer! it make some sense for me now, but i still don't understand what i'm supposed to pass here ``` // Make your query here const position_name = myDatabase.query(‘your_id_and_everything’); const workplace_name = myDatabase.query(‘your_id_and_everything’);``` i use a noSQL database so i'm getting a link to fetch from Heroku – Ron Jul 12 '19 at 14:09
  • @Ron If you have a function `query(id)`, which return the job with the given `id`. Let’s say this function return an object : `{id: 1, position_name: ‘employee’, …}`. You get the result in a variable : `const myJob = query(1);`. Then, you can assign your `position_name` like this : `const position_name = myJob.position_name;`… Or directly use `myJob.position_name`. I wrote this since I have no idea how you query your database. Position_name will, here, have the value ‘employee’ – KawaLo Jul 12 '19 at 14:19
  • @Ron just edited my answer so it may look more clear – KawaLo Jul 12 '19 at 14:50