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 best solutions below

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
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
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
  }