2

I want to access the refs from the render function, and set it to the state.

Here is my code:

export default class App extends Component {
    constructor(props) {
        super(); 
            this.arr = this.generateTimelineArray();
            this.state = {el : 'empty'};
        }
    componentDidMount() {
        this.setState({
          el: this.refs.el
        });
        console.log(this.state.el)
    }
render() {
    return (
      <div  className="timeline__container--line" ref="el" ></div>
    ); 
  }

I can console.log(this.refs.el) and the value is logged. But i have to save it to constructor to pass it to another component.

Problem is that the state is not being changed.

What am i doing wrong?

Thanks in advance

Giedrius
  • 603
  • 1
  • 6
  • 26

2 Answers2

5

setState is async. Docs. If you want to see updated state use callback.

componentDidMount() {
        this.setState({
          el: this.refs.el
        }, () => console.log(this.state.el));
    }
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • It is great, but why do I have to console.log() in order to set state, is it possible to do callback without console.log ? – Giedrius Jul 08 '17 at 13:34
  • @Giedrius You don't have to log. It's just if you want to use **updated** state you need to use a callback. But it is all up to you what exactly you want to do with the new state – Yury Tarabanko Jul 08 '17 at 13:39
  • Ok, last question then, how do i pass an updated state as a prop to another component ? Do i also have to use callback funtion ? – Giedrius Jul 08 '17 at 13:41
  • And you can't have element reference in constructor. Because when constructor is called before render. – Yury Tarabanko Jul 08 '17 at 13:42
  • I recommend you to extract dom information (offset) and pass this value to child. So you start from `this.state = {offset: null}`, then check in render function `this.state.offset && ` and in `componentDidMount` you `this.setState(() => ({offset: getOffset(this.refs.el)}))` – Yury Tarabanko Jul 08 '17 at 13:48
  • 1
    @YuryTarabanko, This question has multiple dupliates existing on SO. Kindly mark them as duplicates instead of answering them – Shubham Khatri Jul 08 '17 at 13:56
0

Like Yury said, this.setState is asynchronous. Apart from a callback function, you can use a boolean state variable and check for that in render:

export default class App extends Component {
    constructor(props) {
        super(); 
            this.arr = this.generateTimelineArray();
            this.state = {el : 'empty', flag : true};
        }
    componentDidMount() {
        this.setState({
          el: this.refs.el,
          flag : false
        });
        console.log(this.state.el)
    }
render() {
    return (
      this.state.flag == false &&
      <div  className="timeline__container--line" ref="el" ></div>
    ); 
  }

This will now only render once the asynchronous setState has completed.

Siddhartha
  • 4,296
  • 6
  • 44
  • 65