React.js useRef / createRef current is always null scrolling

1.7k views Asked by At

I have a problem with assigning reference to a div. I want to scroll to the div for this reference, but the value of .current, is always null/undefined.

import React, { Component, useRef, useEffect } from 'react';
import { Route, BrowserRouter, Redirect } from 'react-router-dom';

class App extends Component {

    myRef = React.createRef();

  componentDidMount() {
    { this.myRef ? this.scrollToRef() : null }
  }

  scrollToRef = () => {
    if(this.myRef.current !== null) 
    // window.scrollTo(0, this.myRef.current.offsetTop);
    window.scrollTo({ behavior: 'smooth', top: this.myRef.current.offsetTop })
  }

  render() {
    return (
<Aux>
      <BrowserRouter>
        <Route
          path="/contact"
          render={() => (
            <Main
              page={<HomePage />}
              tools={<Toolbar />}
              content={
                <Contact ref={this.myRef} />
              }
            />
          )}
        />
      </BrowserRouter>
</Aux>
    );
  }
}

and inside Contact:

import React, { useRef, Component, createRef } from 'react';
import classes from './contact.module.css';

const contact = React.forwardRef((props, ref) => {
  const newRef = useRef();
  return <div className={classes.Contact} ref={ref}>{props.children}</div>
};

I've tried many versions of this and .current is always empty..

enter image description here

React: 16.13.1 I'd like to scroll to the <div className={classes.Contact} ref={ref}> when I click on Contact at toolbar.

EDIT: Right now at scrollToRef() I have reference but window.scrollTo()still doesn't work.. Is it possible that hoc <Aux> harming something? Maybe problem is with re-rendering DOM? Earlier I've tried to use ease-out effect but it also didn't work correctly. It run when I unclick checkbox during debugging but didn't fire when I open new overlap.

3

There are 3 answers

1
ינון רחמים On

you pass to the function setRef the variable (newRef), but in the function definition you wrote (props) as the variable. so you need to change the function to:

  setReference = (newRef) => {
    this.setState({myRef: newRef})
  }

i hope that it will help :)

and by the way you better use forwardRef - https://reactjs.org/docs/forwarding-refs.html

3
Zachary Haber On

I'd recommend to use forwardRef, also, instead of calling functions in the jsx directly, use lifecycle hooks like useEffect and componentDidUpdate

Here's the code using a forwardRef, as you can see it's a simpler way of setting things up in this case.

const Contact = React.forwardRef((props, ref) => {
  return (
    <div className={'Contact'} ref={ref}>
      {props.children}
    </div>
  );
});

class App extends React.Component {
  contactRef = React.createRef();
  state = {};

  scrollToRef = () => {
    if (this.contactRef.current !== null)
      // window.scrollTo(0, this.myRef.current.offsetTop);
      window.scrollTo({
        behavior: 'smooth',
        top: this.contactRef.current.offsetTop,
      });
    console.log(this.contactRef.current);
  };

  render() {
    return (
      <div>
        <Contact ref={this.contactRef} />
        <button onClick={this.scrollToRef}>Scroll</button>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root" />

0
TyRRRax On

Okay, problem fixed.

I changed componentDidMount :

  componentDidMount() {
    // { this.myRef ? this.scrollToRef() : null }
    this.myRef ? this.myRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'center',
      inline: 'center'
    })
      : null
  }