0

I am trying to do auto text animation in my React project. I can make it work in VanillaJS but I don't know how can I do it in React. (I am beginner at React.)

import React, { Component } from 'react'

class AutoTextSection extends Component {
    writeText = () => {
       let idx = 1

       const text = "This is the text sentence."
       document.getElementById('auto-text').textContent = text.slice(0, idx)

       idx++

       if (idx > document.getElementById('auto-text').length) {
        idx = 1
    }

        setTimeout(this.writeText, 1000)
    }
    render() {
       return (
          <section id="auto-text-sec">
            <h2 className="text-light" id="auto-text">
            {this.writeText()}
            </h2>
          </section>
       )
    }
}
  

Just I can see the first letter. Then it throws me this error : TypeError: Cannot set property 'textContent' of null.

  • Use useRef hook to get element. – Bhojendra Rauniyar May 08 '21 at 10:11
  • 1
    use react refs instead of document.getElementById('auto-text') and the error will go away – Nana Koduah May 08 '21 at 10:18
  • Here is an example that uses the React render cycle instead of manipulating the element directly: [“Print” string letter by letter in React](https://stackoverflow.com/questions/65735012/print-string-letter-by-letter-in-react/65735282#65735282) – pilchard May 08 '21 at 21:05

1 Answers1

0

In react, you must use ref to be able to access the DOM element directly.

Change your code:

import React, { Component } from 'react'

class AutoTextSection extends Component {
  constructor(props) {
    super(props);
    this.autoTextRef = React.createRef();
  }

  writeText = () => {
    let idx = 1

    const text = "This is the text sentence."
    this.autoTextRef.current.textContent = text.slice(0, idx)

    idx++

    if (idx > this.autoTextRef.current.length) {
      idx = 1
    }
    setTimeout(this.writeText, 1000)
  }

  render() {
    return (
      <section id="auto-text-sec">
        <h2 className="text-light" ref={this.autoTextRef}>
          {this.writeText()}
        </h2>
      </section>
    )
  }
}

In this line, most likely you wanted to use a text node, which is located inside the DOM element:

if (idx > this.autoTextRef.current.length) {

So use:

if (idx > this.autoTextRef.current.textContent.length) {

But your code still contains bugs. It is better to start typing in the componentDidMount lifecycle hook.

Another obvious problem is that when you call writeText, you will always have idx = 1; Therefore, this state must be endured higher. You can use state for this.

Also in the code there is no condition to terminate the recursive call.

The final minimal working code should look like this:

import React, { Component } from 'react'

class AutoTextSection extends Component {
  constructor(props) {
    super(props);
    this.autoTextRef = React.createRef();
    this.state = {
      idx: 1,
    }
  }

  componentDidMount() {
    this.writeText()
  }

  writeText = () => {

    console.log('writeText')
    const text = "This is the text sentence."
    this.autoTextRef.current.textContent = text.slice(0, this.state.idx)

    this.setState((prev) => ({
      idx: ++prev.idx,
    }))

    if (this.state.idx <= text.length) {
      console.log('writeText recursion')
      setTimeout(this.writeText, 100)
    }
  }

  render() {
    return (
      <section id="auto-text-sec">
        <h2 className="text-light" ref={this.autoTextRef} />
      </section>
    )
  }
}
xom9ikk
  • 2,159
  • 5
  • 20
  • 27