【发布时间】:2020-07-23 17:25:14
【问题描述】:
我有一个固定高度的部分。我不知道组件何时安装(第一次渲染)进入的内容是否适合。如果它不适合,那么我需要呈现一个“阅读更多”按钮。
我最初使用生命周期方法 DidMount/DidUpdate 将其编写为 Class 组件:
类组件
import React, { createRef } from "react"
import styled from "@emotion/styled"
import Section from "../Section"
import ButtonReadMore from "./ButtonReadMore"
import Paragraphs from "./Paragraphs"
const StyledHeightContainer = styled.div`
max-height: 150px;
overflow: hidden;
`
class ParagraphList extends React.Component {
state = {
overflowActive: false,
}
wrapper = createRef() // so we can get a ref to the height container
isOverflowing(el) {
if (el) return el.offsetHeight < el.scrollHeight
}
componentDidMount() {
this.setState({ overflowActive: this.isOverflowing(this.wrapper.current) })
}
componentDidUpdate() {
if (this.wrapper.current && !this.state.overflowActive) {
this.setState({
overflowActive: this.isOverflowing(this.wrapper.current),
})
}
}
handleClick() {
this.setState({ overflowActive: false })
}
render() {
const { moreButtonText, titleText, paragraphs, theme } = this.props
return (
<>
<Section overflowActive={this.state.overflowActive}>
{this.state.overflowActive || !this.wrapper.current ? (
<StyledHeightContainer ref={this.wrapper}>
<Paragraphs paragraphs={paragraphs} />
</StyledHeightContainer>
) : (
<Paragraphs paragraphs={paragraphs} />
)}
</Section>
{overflowActive ?
<ButtonReadMore
onClicked={handleClick.bind(this)}
moreButtonText={moreButtonText}
theme={theme}
/>
: null}
</>
)
}
}
export default ParagraphList
我解释流程的最佳方式:
-
1234563
在
componentDidMount-> 尝试设置溢出标志(这将是错误的,因为此时我们还没有完成渲染,所以引用将为空)。但是无论如何设置标志,我们都会排队一个额外的渲染通道第一次初始渲染完成 -> 我们现在有一个 div 的引用
第二次(排队)渲染发生,触发
componentDidUpdate-> 我们可以计算溢出并在内容溢出时将标志设置为true当用户单击按钮时 -> 将标志设置为 false,这将触发重新渲染,因此
StyledHeightContainer将从 DOM 中删除。
带钩子的功能组件
当我使用 Hooks 将其重写为功能组件时,我最终得到了:
import React, { createRef, useEffect, useState } from "react"
import styled from "@emotion/styled"
import Section from "../Section"
import ButtonReadMore from "./ButtonReadMore"
import Paragraphs from "./Paragraphs"
const StyledHeightContainer = styled.div`
max-height: 150px;
overflow: hidden;
`
const ParagraphList = ({ moreButtonText, titleText, paragraphs, theme }) => {
const [overflowActive, setOverflowActive] = useState(false)
const [userClicked, setUserClicked] = useState(false)
const wrapper = createRef(false) // so we can get a ref to the height container
const isOverflowing = el => {
if (el) return el.offsetHeight < el.scrollHeight
}
useEffect(() => {
if (!userClicked && !overflowActive && wrapper.current) {
setOverflowActive(isOverflowing(wrapper.current))
}
}, [userClicked]) // note: we only care about state change if user clicks 'Read More' button
const handleClick = () => {
setOverflowActive(false)
setUserClicked(true)
}
return (
<>
<Section theme={theme} overflowActive={overflowActive}>
{!userClicked && (overflowActive || !wrapper.current) ? (
<StyledHeightContainer ref={wrapper}>
<Paragraphs paragraphs={paragraphs} />
</StyledHeightContainer>
) : (
<Paragraphs paragraphs={paragraphs} />
)}
</Section>
{overflowActive ?
<ButtonReadMore
onClicked={handleClick.bind(null)}
moreButtonText={moreButtonText}
theme={theme}
/>
: null}
</>
)
}
export default ParagraphList
我很惊讶我需要添加另一个状态 (userClicked),这是我强制进行第二次渲染的方式(即相当于类解决方案中的 componentDidUpdate)。
这是正确的还是有人可以看到更简洁的方法来编写第二个解决方案?
注意
我问的一个原因是因为在控制台中我收到了这个警告:
48:6 warning React Hook useEffect has missing dependencies: 'overflowActive' and 'wrapper'. Either include them or remove the dependency array react-hooks/exhaustive-deps
而且我不 THINK 我想将它们添加到依赖数组中,因为我不想在它们更改时触发渲染...?
【问题讨论】:
-
您能否为您的第二种方法创建一个代码沙箱 (codesandbox.io) 或其他东西?如果需要,我会更容易重构它。
-
好的,我可以试试。给我一些时间,因为我以前没有为 React 做过这些
-
这很简单,只需为此代码创建一个示例/模拟。这样我们就可以玩了:)
-
@ManishSundriyal 我添加了代码,但由于所有数据都是从 GraphQL 查询动态加载的,如果我不模拟所有数据,代码将无法在沙箱中运行
-
@ManishSundriyal 好的,让它在沙盒中工作:codesandbox.io/s/…
标签: javascript reactjs react-hooks