React Refs with TypeScript: Cannot read property 'current' of undefined
up vote
1
down vote
favorite
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
?
reactjs typescript forward-reference react-ref
add a comment |
up vote
1
down vote
favorite
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
?
reactjs typescript forward-reference react-ref
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
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
?
reactjs typescript forward-reference react-ref
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
?
reactjs typescript forward-reference react-ref
reactjs typescript forward-reference react-ref
edited Nov 10 at 18:31
asked Nov 10 at 17:49
J. Hesters
531630
531630
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
!
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 />
}
}
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?
– J. Hesters
Nov 10 at 18:32
I didn't pay attention to Child, but yes, a ref is class instance, not DOM element. You could doel = ReactDOM.findDOMNode(this.testTitleRef.current)
but actually this may not be a good idea to do this withref
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 assignthis.props.scrollRef.current
toh1
ref inside Child.
– estus
Nov 10 at 18:56
Btw, see stackoverflow.com/a/51828976/3731501 , it already suggests something like this, notice that it passesrefProp
and notref
to a child.
– estus
Nov 10 at 19:03
Thank you for your help! You are helping me tremendously. I switchedref
torefProp
and set the ref toh1
now it scrolls, but it scrolls to the top of the document, and not to the header of the child. If I logthis.props.scrollRef.current
it correctly gives out the<h1 />
. Do you have any idea what could cause this?
– J. Hesters
Nov 11 at 7:55
Can you provide a demo to debug?
– estus
Nov 11 at 7:59
|
show 7 more comments
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
!
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 />
}
}
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?
– J. Hesters
Nov 10 at 18:32
I didn't pay attention to Child, but yes, a ref is class instance, not DOM element. You could doel = ReactDOM.findDOMNode(this.testTitleRef.current)
but actually this may not be a good idea to do this withref
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 assignthis.props.scrollRef.current
toh1
ref inside Child.
– estus
Nov 10 at 18:56
Btw, see stackoverflow.com/a/51828976/3731501 , it already suggests something like this, notice that it passesrefProp
and notref
to a child.
– estus
Nov 10 at 19:03
Thank you for your help! You are helping me tremendously. I switchedref
torefProp
and set the ref toh1
now it scrolls, but it scrolls to the top of the document, and not to the header of the child. If I logthis.props.scrollRef.current
it correctly gives out the<h1 />
. Do you have any idea what could cause this?
– J. Hesters
Nov 11 at 7:55
Can you provide a demo to debug?
– estus
Nov 11 at 7:59
|
show 7 more comments
up vote
1
down vote
accepted
!
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 />
}
}
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?
– J. Hesters
Nov 10 at 18:32
I didn't pay attention to Child, but yes, a ref is class instance, not DOM element. You could doel = ReactDOM.findDOMNode(this.testTitleRef.current)
but actually this may not be a good idea to do this withref
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 assignthis.props.scrollRef.current
toh1
ref inside Child.
– estus
Nov 10 at 18:56
Btw, see stackoverflow.com/a/51828976/3731501 , it already suggests something like this, notice that it passesrefProp
and notref
to a child.
– estus
Nov 10 at 19:03
Thank you for your help! You are helping me tremendously. I switchedref
torefProp
and set the ref toh1
now it scrolls, but it scrolls to the top of the document, and not to the header of the child. If I logthis.props.scrollRef.current
it correctly gives out the<h1 />
. Do you have any idea what could cause this?
– J. Hesters
Nov 11 at 7:55
Can you provide a demo to debug?
– estus
Nov 11 at 7:59
|
show 7 more comments
up vote
1
down vote
accepted
up vote
1
down vote
accepted
!
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 />
}
}
!
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 />
}
}
edited Nov 12 at 20:04
answered Nov 10 at 18:07
estus
62.8k2193200
62.8k2193200
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?
– J. Hesters
Nov 10 at 18:32
I didn't pay attention to Child, but yes, a ref is class instance, not DOM element. You could doel = ReactDOM.findDOMNode(this.testTitleRef.current)
but actually this may not be a good idea to do this withref
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 assignthis.props.scrollRef.current
toh1
ref inside Child.
– estus
Nov 10 at 18:56
Btw, see stackoverflow.com/a/51828976/3731501 , it already suggests something like this, notice that it passesrefProp
and notref
to a child.
– estus
Nov 10 at 19:03
Thank you for your help! You are helping me tremendously. I switchedref
torefProp
and set the ref toh1
now it scrolls, but it scrolls to the top of the document, and not to the header of the child. If I logthis.props.scrollRef.current
it correctly gives out the<h1 />
. Do you have any idea what could cause this?
– J. Hesters
Nov 11 at 7:55
Can you provide a demo to debug?
– estus
Nov 11 at 7:59
|
show 7 more 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?
– J. Hesters
Nov 10 at 18:32
I didn't pay attention to Child, but yes, a ref is class instance, not DOM element. You could doel = ReactDOM.findDOMNode(this.testTitleRef.current)
but actually this may not be a good idea to do this withref
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 assignthis.props.scrollRef.current
toh1
ref inside Child.
– estus
Nov 10 at 18:56
Btw, see stackoverflow.com/a/51828976/3731501 , it already suggests something like this, notice that it passesrefProp
and notref
to a child.
– estus
Nov 10 at 19:03
Thank you for your help! You are helping me tremendously. I switchedref
torefProp
and set the ref toh1
now it scrolls, but it scrolls to the top of the document, and not to the header of the child. If I logthis.props.scrollRef.current
it correctly gives out the<h1 />
. Do you have any idea what could cause this?
– J. Hesters
Nov 11 at 7:55
Can you provide a demo to debug?
– estus
Nov 11 at 7:59
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?
– J. Hesters
Nov 10 at 18:32
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?
– J. Hesters
Nov 10 at 18:32
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.– estus
Nov 10 at 18:56
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.– estus
Nov 10 at 18:56
Btw, see stackoverflow.com/a/51828976/3731501 , it already suggests something like this, notice that it passes
refProp
and not ref
to a child.– estus
Nov 10 at 19:03
Btw, see stackoverflow.com/a/51828976/3731501 , it already suggests something like this, notice that it passes
refProp
and not ref
to a child.– estus
Nov 10 at 19:03
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?– J. Hesters
Nov 11 at 7:55
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?– J. Hesters
Nov 11 at 7:55
Can you provide a demo to debug?
– estus
Nov 11 at 7:59
Can you provide a demo to debug?
– estus
Nov 11 at 7:59
|
show 7 more comments
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53241783%2freact-refs-with-typescript-cannot-read-property-current-of-undefined%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown