У меня есть секция с фиксированной высотой. Я не знаю, когда компонент монтируется (сначала рендерится), подойдет ли входящий контент или нет. Если он НЕ подходит, тогда мне нужно отобразить кнопку «Подробнее».
Это выглядит так:
Первоначально я написал это как Компонент класса с использованием методов жизненного цикла DidMount / DidUpdate:
Компонент класса
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
Мой лучший способ объяснить поток:
Когда компонент монтируется, флаг ложен, и у нас нет ссылки на div, поэтому StyledHeightContainer
попытается отрендерить и, таким образом, предоставить ссылку на него
В componentDidMount
-> попытаться установите флаг переполнения (который будет ложным, потому что на этом этапе мы еще не завершили рендеринг, поэтому ссылка будет нулевой). Но, в любом случае, установив флаг, мы ставим в очередь дополнительный проход рендеринга
Первый начальный рендеринг завершается -> теперь у нас есть ссылка на div
2-й (в очереди) рендеринг происходит, запуская 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), как я вынуждаю 2-й рендеринг (ie. эквивалент componentDidUpdate
в решении класса).
Это правильно, или кто-то может увидеть более краткий способ написать 2-е решение?
ПРИМЕЧАНИЕ
O одна из причин, которые я спрашиваю, заключается в том, что в консоли я получаю это предупреждение:
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 Я хочу добавить их в массив зависимостей, так как я не хочу запускать рендеринг при их изменении ...?