如何获得 child 的无状态功能组件的高度?
How do you get the height of a stateless functional component that's a child?
我正在尝试获取无状态 child 组件的高度,以便我能够在 parent Class 中使用它的高度,但我正在获取以下错误:Invariant Violation: Stateless function components cannot have refs.
简化代码
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>
)
有办法吗?
这里是link到a Codepen with the error。
更新:
实际code/context
所以我目前有一个 Header 组件,它看起来像这样:
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>
)
在某些情况下,我想为此 Header 添加逻辑以使其具有动画效果(例如,在主页上,当用户滚动时,我将 Header 动画设置为固定滚动到某个点 - 粘性 header,如果你愿意的话)。
我之前这样做的方法是只为具有特定功能(如上所述)的 Header 和不具有特定功能的 Class 设置一个单独的 Class。但是为了让我的代码保持干爽,我将 Header 分离出来作为它自己的功能性无状态组件,以便将它包装在 Class 中,从而赋予它粘性 header 功能。
这是 Class 的:
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>
)
}
}
这就是这个特定问题的背景 - 我希望这能让事情变得更清楚一些。
P.s 抱歉缺乏上下文,我想答案会比看起来更直接!
这是一个使用 React-waypoint 的解决方案。你可以在这里查看:
https://codesandbox.io/s/qp3ly687
我已经为 onEnter 和 onLeave 添加了去抖,这样它就不会像疯了一样更新状态,这样我们就可以在滚动时优化性能并且它不会锁定应用程序。同样,对于您可以做什么,这是一个非常粗略的想法,并且有很多选项可以改进实施。
=== 上一个
这是一种通过保留无状态组件来解决问题的方法。由于没有进一步解释上下文,这应该让您有机会了解如何获取位于无状态组件中的子组件的高度。
class App extends React.Component {
componentDidMount() {
console.log(this.child.offsetHeight);
}
render() {
return (
<div>
<Wrapper>
<div
ref={child => this.child = child}
style={{height: 500}}>
Some text!!
</div>
</Wrapper>
</div>
)
}
}
const Wrapper = ({children}) => {
return (
<div>
<header>Header</header>
<div>{children}</div>
<footer>Footer</footer>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById("main")
);
好的,首先,感谢大家的回复 - 非常感谢!
我开始按照 Win 的建议实施 react-waypoints
,尽管很明显我需要稍微修改我的代码才能使其正常工作,所以我想我会进行最后一次搜索来尝试并使用 refs
.
找到替代解决方案
我要找的答案其实很简单,来自以下Github issue thread by mnpenner.
解决方案
因为 React 不允许对无状态功能组件进行引用,相反,您可以将功能组件包装在包装器中,同时为包装器提供自己的包装器 ref
,如下所示:
render() {
return (
<div ref={el => {this.childWrap = el;}}>
<Child />
</div>
)
}
然后您可以通过定位包装器来访问组件的高度:
componentDidMount() {
if(this.childWrap && this.childWrap.firstChild) {
let childHeight = this.childWrap.offsetHeight;
console.log(childHeight);
}
}
虽然这可能不是最优雅的解决方案,但它确实暂时解决了我的问题。
这里a Codepen供参考。
我正在尝试获取无状态 child 组件的高度,以便我能够在 parent Class 中使用它的高度,但我正在获取以下错误:Invariant Violation: Stateless function components cannot have refs.
简化代码
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>
)
有办法吗?
这里是link到a Codepen with the error。
更新:
实际code/context
所以我目前有一个 Header 组件,它看起来像这样:
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>
)
在某些情况下,我想为此 Header 添加逻辑以使其具有动画效果(例如,在主页上,当用户滚动时,我将 Header 动画设置为固定滚动到某个点 - 粘性 header,如果你愿意的话)。
我之前这样做的方法是只为具有特定功能(如上所述)的 Header 和不具有特定功能的 Class 设置一个单独的 Class。但是为了让我的代码保持干爽,我将 Header 分离出来作为它自己的功能性无状态组件,以便将它包装在 Class 中,从而赋予它粘性 header 功能。
这是 Class 的:
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>
)
}
}
这就是这个特定问题的背景 - 我希望这能让事情变得更清楚一些。
P.s 抱歉缺乏上下文,我想答案会比看起来更直接!
这是一个使用 React-waypoint 的解决方案。你可以在这里查看: https://codesandbox.io/s/qp3ly687
我已经为 onEnter 和 onLeave 添加了去抖,这样它就不会像疯了一样更新状态,这样我们就可以在滚动时优化性能并且它不会锁定应用程序。同样,对于您可以做什么,这是一个非常粗略的想法,并且有很多选项可以改进实施。
=== 上一个
这是一种通过保留无状态组件来解决问题的方法。由于没有进一步解释上下文,这应该让您有机会了解如何获取位于无状态组件中的子组件的高度。
class App extends React.Component {
componentDidMount() {
console.log(this.child.offsetHeight);
}
render() {
return (
<div>
<Wrapper>
<div
ref={child => this.child = child}
style={{height: 500}}>
Some text!!
</div>
</Wrapper>
</div>
)
}
}
const Wrapper = ({children}) => {
return (
<div>
<header>Header</header>
<div>{children}</div>
<footer>Footer</footer>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById("main")
);
好的,首先,感谢大家的回复 - 非常感谢!
我开始按照 Win 的建议实施 react-waypoints
,尽管很明显我需要稍微修改我的代码才能使其正常工作,所以我想我会进行最后一次搜索来尝试并使用 refs
.
我要找的答案其实很简单,来自以下Github issue thread by mnpenner.
解决方案
因为 React 不允许对无状态功能组件进行引用,相反,您可以将功能组件包装在包装器中,同时为包装器提供自己的包装器 ref
,如下所示:
render() {
return (
<div ref={el => {this.childWrap = el;}}>
<Child />
</div>
)
}
然后您可以通过定位包装器来访问组件的高度:
componentDidMount() {
if(this.childWrap && this.childWrap.firstChild) {
let childHeight = this.childWrap.offsetHeight;
console.log(childHeight);
}
}
虽然这可能不是最优雅的解决方案,但它确实暂时解决了我的问题。
这里a Codepen供参考。