[英]React Hooks : Stateless component VS Class component in the era of Hooks - which is better and recommended?
[英]A better way to write this React Class Component with Hooks?
我有一個固定高度的部分。 我不知道組件何時安裝(第一次渲染)進入的內容是否適合。 如果它不適合,那么我需要呈現一個“閱讀更多”按鈕。
我最初使用生命周期方法 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
我解釋流程的最佳方式:
當組件掛載時,標志為 false 並且我們沒有對 div 的引用,因此StyledHeightContainer
將嘗試渲染並因此為其提供參考
在componentDidMount
-> 嘗試設置溢出標志(這將是錯誤的,因為此時我們還沒有完成渲染,所以 ref 將為空)。 但是無論如何設置標志,我們排隊一個額外的渲染通道
第一次初始渲染完成 -> 我們現在有一個 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
我很驚訝我需要添加另一個 state(userClicked),這是我強制進行第二次渲染的方式(即,相當於 class 解決方案中的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
而且我不想將它們添加到依賴項數組中,因為我不想在它們更改時觸發渲染...?
在解決查詢時我真的很享受。
這是實現: https://codesandbox.io/s/react-using-hooks-in-section-component-5gibi?file=/src/ParagraphList.js
首先,我在想
useEffect(() => {
setOverflowActive(isOverflowing(wrapper.current));
}, [wrapper]);
但是如果我們這樣做,它將再次調用 useEffect,就像我們單擊 Read more 按鈕時一樣。 因為它是在比較包裝器的引用而不是它的值。
因此,為了避免引用比較,我們必須使用 useCallback 掛鈎。
const isOverflowingNode = node => {
return node.offsetHeight < node.scrollHeight;
};
const wrapper = useCallback(node => {
if (node !== null) {
setOverflowActive(isOverflowingNode(node));
}
}, []);
我遇到了漂亮的討論: https://github.com/facebook/react/issues/14387
更多信息: https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node
謝謝你的問題:)
您可以添加一個額外的useEffect(() => (...),[])
,其作用類似於componentDidMount()
。 還有另一個useEffect(() => (...))
,其作用類似於componentDidUpdate()
。 然后你應該能夠擺脫userClicked
。
這是關於生活方式方法如何與鈎子一起使用的一個很好的鏈接。 https://dev.to/trentyang/replace-lifecycle-with-hooks-in-react-3d4n
useEffect(() => {
setOverflowActive(isOverflowing(wrapper.current));
}, []);
useEffect(() => {
if (!overflowActive && wrapper.current) {
setOverflowActive(isOverflowing(wrapper.current))
}
});
如果您希望在布局之后進行更新,則第二個可能需要是useLayoutEffect
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.