Ryan Santos
Ryan Santos

Reputation: 387

Is there a way to redirect to a default url hash with React Router?

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

Answers (2)

Ryan Santos
Ryan Santos

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

Cat_Enthusiast
Cat_Enthusiast

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

Related Questions