6

I'm building a React application using TypeScript.

I want to create button, that scrolls to a header of a child component on my main page.

I've created a ref in the child component, following this stack overflow answer and (tried to) use forward refs to access it on my parent component.

export class Parent extends Component {
  private testTitleRef!: RefObject<HTMLHeadingElement>;

  scrollToTestTitleRef = () => {
    if (this.testTitleRef.current !== null) {
      window.scrollTo({
        behavior: "smooth",
        top: this.testTitleRef.current.offsetTop
      });
    }
  };

  render() {
    return <Child ref={this.testTitleRef} />
  }
}

interface Props {
  ref: RefObject<HTMLHeadingElement>;
}

export class Child extends Component<Props> {
  render() {
    return <h1 ref={this.props.ref}>Header<h1 />
  }
}

Unfortunately when I trigger scrollToTestTitleRef I get the error:

Cannot read property 'current' of undefined

Meaning that the ref is undefined. Why is that? What am I doing wrong?

EDIT: Estus helped me to create the ref. But when I trigger the scrollToTestTitleRef() event, it doesn't scroll. When I console.log this.testTitleRef.current I get the output:

{"props":{},"context":{},"refs":{},"updater":{},"jss":{"id":1,"version":"9.8.7","plugins":{"hooks":{"onCreateRule":[null,null,null,null,null,null,null,null,null,null,null,null],"onProcessRule":[null,null,null],"onProcessStyle":[null,null,null,null,null,null],"onProcessSheet":[],"onChangeValue":[null,null,null],"onUpdate":[null]}},"options":{"plugins":[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]}},"sheetsManager":{},"unsubscribeId":null,"stylesCreatorSaved":{"options":{"index":-99999999945},"themingEnabled":false},"sheetOptions":{},"theme":{},"_reactInternalInstance":{},"__reactInternalMemoizedUnmaskedChildContext":{"store":{},"storeSubscription":null},"state":null}

Note: I deleted the keys of cacheClasses, _reactInternalFiber and __reactInternalMemoizedMaskedChildContext, because they contained cyclic dependencies.

So current doesn't seem to have a key of offsetTop. Does this maybe have something to do with the fact that in my real application the child component is wrapped inside material-ui's withStyle and React-Redux' connect?

1 Answer 1

3

! non-null assertion operator suppresses the actual problem. There is no way in JavaScript/TypeScript how testTitleRef property could be assigned from being used as <Child ref={this.titleRef} />, so it stays undefined (there's also inconsistency with testTitleRef and titleRef).

It should be something like:

  private testTitleRef: React.createRef<HTMLHeadingElement>();

  scrollToTestTitleRef = () => {
      if (!this.testTitleRef.current) return;

      window.scrollTo({
        behavior: "smooth",
        top: this.testTitleRef.current.getBoundingClientRect().top + window.scrollY
      });
  };
  render() {
    return <Child scrollRef={this.testTitleRef} />
  }

and

export class Child extends Component<Props> {
  render() {
    return <h1 ref={this.props.scrollRef}>Header<h1 />
  }
}
Sign up to request clarification or add additional context in comments.

12 Comments

Thank you! The inconsistency was just because I jotted down that examply quickly for this question. I created the ref your way, and it doesn't crash anymore. It still doesn't work. I added more info to the question. Could you please help me further?
I didn't pay attention to Child, but yes, a ref is class instance, not DOM element. You could do el = ReactDOM.findDOMNode(this.testTitleRef.current) but actually this may not be a good idea to do this with ref because this wouldn't work at all with function component (they have no refs), also a component may render to multiple DOM elements or no elements at all. I'd suggest to refactor the code to make components decide themselves which element should expose as scroll box, e.g. <Child scrollRef={this.testTitleRef} /> and assign this.props.scrollRef.current to h1 ref inside Child.
Btw, see stackoverflow.com/a/51828976/3731501 , it already suggests something like this, notice that it passes refProp and not ref to a child.
Thank you for your help! You are helping me tremendously. I switched ref to refProp and set the ref to h1 now it scrolls, but it scrolls to the top of the document, and not to the header of the child. If I log this.props.scrollRef.current it correctly gives out the <h1 />. Do you have any idea what could cause this?
Can you provide a demo to debug?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.