Reputation: 2709
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
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