Reputation: 387
I am using React Router 5.
I have a path /item/:id
. When I navigate to this path I want to be able to load the page with a default hash identifier. i.e. /item/:id#123
For more context I have a list of steps on my page associated with an item. Every time a user selects a different step, the hash changes accordingly like so:
step 1 -> /item/123#1
step 2 -> /item/123#2
step 3 -> /item/123#3
Here's a rough implementation of my component code:
import { withRouter } from 'react-router-dom'
import steps from './steps'
const ItemPage = ({ history, location }) => {
const { hash, pathname } = location
const changeURLhash = idx => {
history.push({ pathname: pathname, hash: idx.toString() })
}
return (
<ul>
{steps.map(step => (
<li key={i} onClick={changeURLhash}>
{step.title}
</li>
))}
</ul>
)
}
I am able to change the hash after selecting a step but on initial page load when nothing has been clicked yet there is no hash in the url path. I need to change this because step 1 is selected by default on page load.
What would be the best way to do this? Thanks in advance!
Upvotes: 1
Views: 4659
Reputation: 387
I've been able to solve this by using history.replace in a useEffect hook.
When the page loads and there is no hash identifier in the url, I replace the current path in the history stack with a new path containing a default hash
useEffect(() => {
if (!hash) history.replace({ pathname: pathname, hash: '0' })
}, [hash, history, pathname])
Using replace instead of push here eliminates the unnecessary extra entry that gets added to the history stack.
Upvotes: 0
Reputation: 15688
Without being able to see your code, it's difficult to cater an exact solution to your problem. However, I've created a sandbox for you to demonstrate how you might do this.
https://codesandbox.io/s/gifted-sinoussi-5eomb
Essentially, your Item
component needs a combination of useState
and useEffect
import React, { useState, useEffect } from "react";
const Item = ({ steps }) => {
const [currentStep, setCurrentStep] = useState(1);
useEffect(() => {
const path = window.location.href;
const step =
path.indexOf("#") !== -1 ? path.slice(path.indexOf("#") + 1) : "";
if (step) {
setCurrentStep(step);
}
}, []);
const handleOnClick = step => {
setCurrentStep(step);
};
const createSteps = () => {
return steps.map((step, index) => {
return (
<div
className={step == currentStep ? "currentStep" : "step"}
key={index}
onClick={() => handleOnClick(step)}
>
<h4>Step: {step}</h4>
<input />
</div>
);
});
};
return <div>{createSteps()}</div>;
};
export default Item;
You have an state to keep track of the current step. And a mark-up creator function to apply the highlighted class to the active item. Then in the useEffect()
you would just extract the step number (character after the #) and set your state to that value. That will re-render the component, and your mark-up creator will apply the class to that step.
Upvotes: 1