Home > Back-end >  React Ref null on conditional render
React Ref null on conditional render

Time:05-13

React ref animationRef is null whenever I conditionally render an element. It works fine outside of the condition. Is there any way to get the ref to have a value?

const When =
    (props: { children: React.ReactElement, condition: boolean })
        : React.ReactElement => props.condition ? props.children : <></>


export default class QLButton extends React.Component<{title: string}> {
    state: State;
    animationRef: React.RefObject<HTMLElement>
    constructor(props: {title: string}) {
        super(props);
        this.animationRef = React.createRef();
        this.state = {
            title: props.title,
            collapsed: true,
            animating: false
        }
    }

    render(): React.ReactElement {
        return <>
            <button onClick={this.onClick} className="ql-dropdown-btn">

                <When condition={this.state.animating}>
                    <div className="typing-text">
                        <span className="cursor" ref={this.animationRef} />
                    </div>
                </When>

            </button>
        </>
    }

CodePudding user response:

React state updates are not synchronous.

When you call setState, you're requesting that React update the state. The actual state update happens later. This means that immediately after the setState call, the element

<span className="cursor" ref={this.animationRef} />

hasn't been created or rendered yet, so the ref points to null.

In short, if the item is removed from DOM, ref will point to null.

Also, ref is set to the DOM node when it’s mounted, and set to null when the DOM node is removed. So everytime there is a re-render, it's set to null then initialized again.

See this example, when you click on the text, state is updated (re-render) then ref is set to null then back to the element -

const {useState, useCallback} = React;

function App() {
  const ref = useCallback((e) => console.log("ref", e), []);
  const [iter, rerender] = useState(0);
  return (
    <div ref={ref} key={iter} onClick={() => rerender(iter   1)}>
      click to remount
    </div>
  );
}


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

CodePudding user response:

Solved by using componentDidUpdate instead of directly after a setState call, as setState is not synchronous as Aseem mentioned

   onClick() {
      this.setState(...);
      //componentDidUpdate is called
   }

   componentDidUpdate() {
      // do something
   }
  • Related