I'm trying to get the height of a stateless child component, so that I'm able to use its height within a parent Class, but I am getting the following error: Invariant Violation: Stateless function components cannot have refs.
Simplified code
Parent class
class App extends React.Component {
componentDidMount() {
console.log(this.header);
}
render() {
return (
<Child ref={component => this.header = component} />
)
}
}
Child
const Child = () => (
<header ref="header">
Some text
</header>
)
Is there a way to do this?
Here is a link to a Codepen with the error.
Update:
Actual code/context
So I've currently got a Header component, which looks like so:
export const Header = ({dateDetailsButton, title, logo, backButton, signUpButton, inboxButton, header}) => (
<header header className="flex flex-row tc pa3 bb b--light-gray relative min-h-35 max-h-35">
{signUpButton ? <Link to="/edit-profile-contact" href="#" className="flex z-2"><AddUser /></Link> : null }
{backButton ? <BackButton className="flex z-2" /> : null }
<h1 className={logo ? "w-100 tc dark-gray lh-solid f4 fw5 tk-reklame-script lh-solid ma0" : "w-100 tc dark-gray lh-solid f4 fw4 absolute left-0 right-0 top-0 bottom-0 maa h1-5 z-0"}>{title}</h1>
{dateDetailsButton ? <Link to="/date-details" className="absolute link dark-gray right-1 top-0 bottom-0 maa h1-5 z-2">Details</Link> : null }
{inboxButton ? <Link to="/inbox" href="#" className="flex mla z-2"><SpeechBubble /></Link> : null}
</header>
)
In some instances, I want to add logic to this Header to animate it (for example, on the homepage when the user scrolls, I am animating the Header to become fixed when they scroll past a certain point - a sticky header, if you will).
The way I've done this before was to just have a separate Class for a Header with particular functionality (as described above) and one without. But in order to keep my code DRY I have separated out the Header to be its own functional stateless component, with the view to wrap it in Class that gives it the sticky header functionality.
Here's the Class for that:
export default class FeedHeader extends React.Component {
constructor(props) {
super(props);
this.handleScroll = this.handleScroll.bind(this);
this.state = {
scrolledPastHeader: null,
from: 0,
to: 1,
}
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
this.setState({navHeight: this.navHeight.offsetHeight});
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll(e) {
const { navHeight, scrolledPastHeader } = this.state;
const wy = document.body.scrollTop;
if (wy < navHeight) this.setState({scrolledPastHeader: null})
else if (wy > navHeight && wy < 100) {
this.setState({scrolledPastHeader: false})
}
else this.setState({scrolledPastHeader: true})
}
getMotionProps() {
const { scrolledPastHeader } = this.state;
return scrolledPastHeader === false ?
{
style: {
value: this.state.from
}
}
: {
style: {
value: spring(this.state.to)
}
}
}
render() {
const { brand, blurb } = this.props;
const { scrolledPastHeader } = this.state;
return (
<Motion {...this.getMotionProps()}>
{({value}) => {
return (
<Header ref="child" title={this.props.title} backButton={false} signUpButton inboxButton logo/>
);
}}
</Motion>
)
}
}
So this is the context for this particular problem - I hope that makes things a little more clearer.
P.s sorry for the lack of context, I imagined the answer would be more straight forward than what it seems!