alala_emily
alala_emily

Reputation: 13

Refresh issue: Use pathname & array of urls to set next and previous links in React/Gatsby

I am using an array of links to paginate because I want them in a specific order. This all works fine, if I navigate to any of the features pages I can paginate through them; but if I refresh a page the Next href defaults to [0] or 'features/safety-training' and Prev ends up being undefined. I am assuming this is something to do with how useEffect works but I am not sure how to fix it.

   const [position, setPosition] = useState(null);
   const [next, setNext] = useState(null);
   const [prev, setPrev] = useState(null);

     let theArray =[
        "/features/safetytraining", 
        "/features/certificatetracking", 
        "/features/policies", 
        "/features/standard-operating-procedures", 
        "/features/competencies", 
        "/features/contractor-orientations", 
        "/features/worksite-screening", 
        "/features/inspections", 
        "/features/incident-reporting", 
        "/features/behavior-observations", 
        "/features/bulletins", 
        "/features/suggestions", 
        "/features/polls", 
        "/features/whistleblower"
    ]

    useEffect(() => {

        let pathname = window.location.pathname

        //Check the array for pathname

        let checkPosition = theArray.indexOf(pathname);

       //Set the position to pathname index

        setPosition(checkPosition)

    });

       //Update next/prev when position changes

    useEffect(() => {

        setNext(theArray[position +1])

        setPrev(theArray[position -1])

    }, [position]);  


Here's the links:

            <>
            <button 
            style={{marginRight: '15px'}}>
                <Link to={prev}>Prev</Link>
                </button>

            <button>
                <Link to={next}>Next</Link>
                </button>
            </> 

Upvotes: 1

Views: 276

Answers (1)

coreyward
coreyward

Reputation: 80041

You don’t need state or side effects (useEffect) for any of this:

// Pages don't change and don't depend on props or state,
// so let's define w/ `const` and extract from the
// component to avoid recreating on each render:
const pages = [
  "/features/safetytraining",
  "/features/certificatetracking",
  "/features/policies",
  "/features/standard-operating-procedures",
  "/features/competencies",
  "/features/contractor-orientations",
  "/features/worksite-screening",
  "/features/inspections",
  "/features/incident-reporting",
  "/features/behavior-observations",
  "/features/bulletins",
  "/features/suggestions",
  "/features/polls",
  "/features/whistleblower",
];

const Example = () => {
  // This is extraordinarily cheap to compute and will not
  // change without a re-render occurring, so we don't need
  // for it to be state:
  const position = typeof window === "undefined"
    ? 0
    : pages.indexOf(window.location.pathname);

  // Avoid using an invalid index position
  const prevPath = position <= 0 ? pages[pages.length] : pages[position - 1];
  const nextPath = position >= pages.length - 1 ? pages[0] : pages[position];

  return (
    <div>
      <button style={{ marginRight: "15px" }}>
        <Link to={prevPath}>Prev</Link>
      </button>

      <button>
        <Link to={nextPath}>Next</Link>
      </button>
    </div>
  );
};

Your original code was creating a number of issues:

  • Each time you call a setter function (e.g. setPosition) React queues a re-render of the component
  • On the initial render, position, prev, and next were all null
  • When doing the server-side render, useEffect is not called, so your links would be broken on the initial load
  • Once the component was mounted client-side, both useEffect hooks would be called
    1. The first hook would call setPosition, prompting a re-render where position was set (prev and next would still be null); since there is no dependency array, the first hook is called again, and setPosition is called again
    2. The second hook would be called again since position changed and call setPrev and setNext, prompting two more re-renders
    3. On the next render, prev would be set but next would not be
    4. On the next render, next would finally be set as expected

Upvotes: 1

Related Questions