reactjsfunctionbuttonreact-hooksreact-scroll

Smooth Scroll + Trigger Function with OnClick of Button


import React, { useState, useRef } from "react";
import { Link, animateScroll as scroll } from "react-scroll";

function Acc(props) {
  const content = useRef(null);

  const [setActive, setActiveState] = useState("");
  const [setScroll, setScrollState] = useState();

  function toggle() {
    setActiveState(setActive === "" ? "active" : "");
    setScrollState(setScroll === "active" ? <Link to={id} smooth={true} duration={500} spy={true} exact={true}></Link> : );
  }

  return (
    <div className="a1" id={props.id}>
      <div className="a2">
        <div className="a3">
          <button className="button" onClick={toggle}>
            //image goes here
          </button>
        </div>
      </div>
    </div>
  );
}

export default Acc;

Why am I getting this error?

./src/components/Acc.js
SyntaxError: /Users/me/Desktop/School/aapp/src/components/Acc.js: Unexpected token (24:121)

  23 |     setRotateState(setActive === "active" ? "icon" : "icon rotate");
> 24 |     setScrollState(setScroll === "active" ? <Link to={id} smooth={true} duration={500} spy={true} exact={true}></Link> : );
     |                                                                                                                          ^
  25 |   }
  26 |
  27 |   return (

How do I fix it? The aim is to create smooth scroll action to the necessary div (classname='a1' which has an id) when the button is clicked. The button must smooth scroll as well as trigger toggle().

Any help would be greatly appreciated !


Solution

  • The error as Ken White pointed out is because of this:

    setScrollState(setScroll === "active" ? <Link to={id} smooth={true} duration={500}spy={true} exact={true}></Link> : );
    

    Scroll to the end of the line for the problem.

    If you're using a ternary statement you need to have something on the left and the right side of the colon :.

    So something like this would be correct:

    setScrollState(
      setScroll === "active" ? (
        <Link to={id} smooth={true} duration={500} spy={true} exact={true}></Link>
      ) : null
    );
    

    Doesn't have to be null, but you need to put something there.


    Now I'm not entirely sure what you're trying to do, but from your code it seems you want to conditionally render a Link on button press. And when you press on it, the page should smooth scroll to an Element with a particular name.

    Here's a simple example combining conditional rendering and smooth scrolling with react-scroll:

    import React, { useState, useRef } from "react";
    import { Element, Link, animateScroll as scroll } from "react-scroll";
    
    function Acc(props) {
      const [isActive, setIsActive] = useState(false);
    
      function toggle() {
        setIsActive(true);
      }
    
      return (
        <div className="a1" id={props.id}>
          <div className="a2">
            <div className="a3">
              <button className="button" onClick={toggle}>
                show link
              </button>
              {isActive && (
                <Link
                  to="scroll-to-element"
                  smooth={true}
                  duration={500}
                  spy={true}
                  exact="true"
                >
                  Link
                </Link>
              )}
            </div>
          </div>
        </div>
      );
    }
    
    export default function App() {
      return (
        <div className="App">
          <Acc />
          {[...Array(100).keys()].map((el) => {
            return (
              <Element key={el} name={el} className="element">
                {el}
              </Element>
            );
          })}
          <Element name="scroll-to-element" className="element">
            Scroll to element
          </Element>
        </div>
      );
    }
    

    I've replaced your setActive and setScroll states with a single isActive state. At least for conditionally rendering the Link you don't need more than one piece of state.

    sandbox example

    Update

    If you want the button to scroll there is no need for the Link and you could just do something like this:

    import "./styles.css";
    import React from "react";
    import { Element, scroller } from "react-scroll";
    
    function Acc(props) {
      function toggle(e) {
        scroller.scrollTo("scroll-to-element", {
          smooth: true,
          duration: 500,
          spy: true,
          exact: true
        });
      }
    
      return (
        <div className="a1" id={props.id}>
          <div className="a2">
            <div className="a3">
              <button className="button" onClick={toggle}>
                scroll
              </button>
            </div>
          </div>
        </div>
      );
    }
    
    export default function App() {
      return (
        <div className="App">
          <Acc />
          {[...Array(100).keys()].map((el) => {
            return (
              <Element key={el} name={el.toString()} className="element">
                {el}
              </Element>
            );
          })}
          <Element name="scroll-to-element" className="element">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ut
            vulputate lectus. Nam cursus semper mauris eget mattis. In nisl nibh,
            tempus quis lorem vitae, egestas imperdiet enim. Curabitur dictum libero
            nibh, ac tempus massa tincidunt elementum. Donec condimentum lacinia
            tortor in posuere. Quisque faucibus hendrerit nibh et convallis. Sed
            lacinia, massa id eleifend cursus, mi nulla sollicitudin dolor, eu
            posuere sapien nisi et sapien. Sed pellentesque lorem sed velit vehicula
            semper. Maecenas finibus, dolor hendrerit pulvinar feugiat, sem urna
            dictum magna, placerat dignissim est lorem vitae justo. Integer non nibh
            nulla.
          </Element>
        </div>
      );
    }
    

    sandbox example