Jakub
Jakub

Reputation: 2709

How to redirect a link using History to a page

In my React app, I added useHistory to be able from my page Rovers to be redirect onClick of a button to one specific page. That button is contained inside 3 divs where onClick takes the name of the Rover and open the right page related to that Rover selected.

On that page, I would like to add 2 links which will redirect to the other 2 Rovers so the user no need to back to the Rovers page all the time.

My issue I'm getting this error:

TypeError: Cannot read property 'roverName' of undefined 

The code flow is as follow:

The rover button onClick is inside my Card component which shows the rover info and where I'm using History below

export default function CardComponent({
  name
  // other props
}) {   
  const history = useHistory();

  const redirectToRoverPage = () => {
    return history.push({
      pathname: `/mars-rovers/rover/${name}`,
      state: { roverName: name },
    });
  };

  return (
    <Col md={4} className="mb-5">
      <Card className="border-black mt-3">
          {/* some rover content here */}
          <Button variant="success" onClick={() => redirectToRoverPage()}>
            ENTER
          </Button>
        </Card.Body>
      </Card>
    </Col>
  );
}

Then inside my Rover page where I use 2 components

Index

export default function Rover(data) {
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    Delayed.delay(() => {
      setIsLoading(false);
    }, 3000);
  });

  const roverName = `${data.location.state.roverName}`;
  return isLoading ? (
    <RoversLoader />
  ) : (
    <Container>
      <RoverIntro roverName={roverName} />
    </Container>
  );
}

RoverIntro

export default function RoverIntro({ roverName }) {
  return (
    <JumboTronWrapper>
      <Jumbotron className="jumbotron-intro">
        <div className="d-flex align-items-center">
          <h1>{roverName}</h1>
          <span className="ml-auto">
            <Link to="/mars-rovers">Rovers</Link>
            <Link to="/mars-rovers/rover/Curiosity">Curiosity</link> <-- Here clicking give me that error I showed above -->
            OTHER LINKS 
          </span>
        </div>
        <RoverText roverName={roverName} />
      </Jumbotron>
    </JumboTronWrapper>
  );
}

What I would like to understand as the first time I'm using History what should I do to permit the redirect from one Rover page to another rover page.

I want to avoid going back to mars-rovers main page all the time and allow a user to go directly to the other rover.

If you need it I can show more codes regarding this flow.

Upvotes: 0

Views: 71

Answers (1)

Qwerty
Qwerty

Reputation: 31949

The problem is that in your Card component, you navigate using history api and you specifically set state in the push.

  state: { roverName: name }

But then, in the Rover pages, you use <Link/> component without setting any state. This is why when you attempt to read data.location.state.roverName it throws.

I advice not to use the history api directly at all, but instead render a <Link/> in your Card.

And then within all your <Link/> components, specify the state inside to={object}, docs.

<Link
  to={{
    pathname: `/mars-rovers/rover/${name}`,
    state: { roverName: name },
  }}
/>

To answer your comment:

Why make life difficult? JavaScript is beautiful powerful language without limits.

export default function Links({ roverName: currentRover }) {
  const rovers = {
    curiosity: 'Curiosity',
    opportunity: 'Opportunity',
    spirit: 'Spirit',
  };

  return Object.keys(rovers).map(key => {
    if (rovers[key] === currentRover) return null

    const roverName = rovers[key]

    return (
      <Badge pill variant="primary" className="mr-2">
        <Link
          className="text-white"
          to={{
            pathname: `/mars-rovers/rover/${roverName}`,
            state: { roverName },
          }}>
          {roverName}
          </Link>
      </Badge>
    )

  })
}

Anyway, you should restructure your data a bit, you make adding new items in the future difficult. You should centralize your state and avoid duplication as is the case with your rovers definition in this <Links/> component. Instead, pass it via props and calculate the names from your original data like this:

const roverData = {
  curiosity: {
    name: 'Curiosity',
    landingDate: '',
    launchDate: '',
    maxDate: '',
    maxSol: '',
    status: '',
    totalPhotos: '',
  },
  opportunity: {
      name: 'Opportunity',
      // ...
    },
    spirit: { 
      name: 'Spirit',
      // ...
  },
};

const roverKeys = Object.keys(roverData)
const roverNames = roverKeys.map(key => roverData[key].name)

PS: It is better to pass a key of active rover to the link and in the location.state instead of its name. Keys are unique and also it allows you to manipulate objects directly without having to traverse the items and search for a particular value.

Upvotes: 1

Related Questions