Scroll onClick using React.fowardRef is not working

1.4k views Asked by At

I would like to know how to use React refs to navigate to a specific component

function App() {
  let CompetencesRef = React.createRef();
  let ExperiencesRef = React.createRef();
  let FormationRef = React.createRef();
  let RecoRef = React.createRef();
  let refs = { CompetencesRef, ExperiencesRef, FormationRef, RecoRef }
  let scrollToRef = (reference) => {
    console.log(reference)
    reference.current.scrollIntoView({ behavior: 'smooth' })
  }
  return (
    <ParallaxProvider>
      <ThemeProvider theme={theme}>
        <div className="App">

          <div className="hero">
            <HeaderApp refs={refs} scrollToRef={scrollToRef} />
            <ApprochApp />
          </div>
          <Apropos />
          <Competences ref={CompetencesRef} />
          <Experiences ref={ExperiencesRef} />
          <Formation ref={FormationRef} />
          <Recom ref={RecoRef} />
          <Contact />
          <Footer />

        </div >
      </ThemeProvider>
    </ParallaxProvider>
  );
}

export default App;

AppHeader

const AppHeader = (props, refs, scrollToRef) => {
    console.log(props)

    return (
        <div >
            <Headroom>
                <MenuApp refs={props.refs} scrollToRef={props.scrollToRef} />
            </Headroom>

        </div>

    )
}
export default AppHeader

AppMenu

const MenuApp = (props, refs, scrollToRef) => {
    console.log(props)
    return (
        <div className="menu sticky-inner grid-container">
            <div className="desktop-menu">
                <div className="menu-item a-propos" ref={props.refs.CompetencesRef} onClick={() => props.scrollToRef(props.refs.CompetencesRef)}>
                    <p className='button'>Compétences</p>
                </div>
                <div className="menu-item competences" ref={props.refs.ExperiencesRef} onClick={() => props.scrollToRef(props.refs.ExperiencesRef)} >
                    <p className='button'>Experiences </p>
                </div>
                <div className="menu-item experiences" ref={props.refs.FormationRef} onClick={() => props.scrollToRef(props.refs.FormationRef)}>
                    <p className='button'>Formation</p>
                </div>
                <div className="menu-item formation" ref={props.refs.RecoRef} onClick={() => props.scrollToRef(props.refs.RecoRef)}>
                    <p className='button'>Recomendations </p>
                </div>
            </div>
            <p className="mobile-menu">
                <MenuIcon />
            </p>
            <div className="github-ico">
                <GitHubIcon />
            </div>
            <div className="linkedin-ico">
                <LinkedInIcon />
            </div>
            <div className="contact">
                <div className='contact-btn'>
                    <span className="contact-ico"> <MessageIcon /></span>  <span style={isBrowser ? { display: 'block' } : { display: 'none' }} > contact </span>
                </div>
            </div>
        </div>
    )
}
export default React.forwardRef(MenuApp) 

I'm using a ClickHandler in Apps.js with scrollIntoView. But actually when I click view does not scroll.

I would not use packages like react-scroll

3

There are 3 answers

0
Chris On BEST ANSWER

There are two problems in your code:

First, there is a difference in using ref for function components and class components. Using ref on functional components will yield an unexpected result because they don't have an instance.

You should be using forwardRef for all section components and make sure the ref is placed on the wrapping element.

The second problem is that you are using the refs for two components. Refs should only be assigned to a single component (maybe not in all cases). In this case, you want a reference to the section components to get a hold on the HTML element.

So remove the ref props in the AppMenu component:

<div className="menu-item a-propos" onClick={() => props.scrollToRef(props.refs.CompetencesRef)}>
  <p className='button'>Compétences</p>
</div>

Although it will work with refs, you might be considering an easier setup. E.g. by using id's.

If you do want to keep using refs, I would also suggest that you don't pass the refs object to child components. You actually don't want the child component to "know" about higher components in the tree.

For example, you can mitigate this by handling the onClick callback in the App component.

3
Dennis Vash On

Here is a minimal reproducible example (without all code noise):

function App() {
  const competencesRef = React.useRef();
  const refs = { competencesRef /* ExperiencesRef, FormationRef, RecoRef */ };
  const scrollToRef = (reference) => {
    reference.current.scrollIntoView({ behavior: "smooth" });
  };
  return (
    <>
      <AppHeader refs={refs} scrollToRef={scrollToRef} />
      <Competences ref={competencesRef} />
    </>
  );
}

const Competences = React.forwardRef((props, ref) => {
  return <div ref={ref}>...</div>;
});

const AppHeader = (props) => {
  return <MenuApp refs={props.refs} scrollToRef={props.scrollToRef} />;
};

const MenuApp = (props) => {
  return (
    <div
      ref={props.refs.competencesRef}
      onClick={() => props.scrollToRef(props.refs.competencesRef)}
    >
      <p className="button">Compétences</p>
    </div>
  );
};

The problem with your code is that you use React.createRef instead of React.useRef (you can search for the differences), and you need React.forwardRef only for components using the ref prop like Competences.

0
Agustin Moles On

Try this in your scrollToRef function:

window.scrollTo(0, reference.current.offsetTop);