需求
後臺項目,使用Ant Design Pro, 有這樣一個需求:有一個表格,寬度是自適應的,表格中有一列是備註,文本長度不定,我們希望在文本過長的時候,使用省略樣式(ellipsis),同時鼠標懸浮時有提示框展示完整文本。
設計
我計劃設計一個React Hooks組件,嵌在表格裏面,實現文本的自適應省略樣式。
單元格寬度
這一列我們只能使用相對寬度,因爲整個表格是自適應寬度的,如果用固定寬度,可能在大屏上,這一列顯得很窄。
這裏我用百分比,同時在頁面組件維護一個寬度狀態,在mounted之後,按百分比計算這一列的寬度並更新狀態,如:clientWidth * 0.2。另外,監聽window resize事件,更新寬度狀態。
組件寬度
列寬計算出來之後,會通過props傳給組件,有了寬度,利用:text-overflow: ellipsis; 就可以實現動態寬度的文本省略了。
提示框
這個提示框是在超長時纔有,不超長時是沒有的。這個是比較麻煩的一點,因爲你要知道當前是不是在超長省略狀態,我們需要這個狀態來設置是否加提示框。
爲了實現這個功能,我加了兩個Hooks狀態:是否超長、文本真實寬度。
- 文本真實寬度:渲染完成後,通過dom獲取元素寬度。
- 是否超長:比較文本真實寬度和組件的寬度。
實現
這裏我就直接貼代碼了,在後面會理一下關鍵點。
export default function LineEllipsis(props) {
const { children, width = '200px', ...restProps } = props;
const [textWidth, setTextWidth] = useState(0);
const [isOverflow, setIsOverflow] = useState(false);
const textRef = useRef(null);
const textStyles = {
width,
display: 'inline-block',
overflow: 'hidden',
wordWrap: 'nowrap',
textOverflow: 'ellipsis',
};
useEffect(
() => {
if (textRef) {
const { current } = textRef;
const clientWidth = current.clientWidth;
setTextWidth(clientWidth);
if (!isOverflow && clientWidth > parseInt(width)) {
setIsOverflow(true);
} else if (isOverflow && clientWidth < parseInt(width)) {
setIsOverflow(false);
}
}
},
[children]
);
useEffect(
() => {
if (textRef && textWidth > 0) {
if (!isOverflow && textWidth > parseInt(width)) {
setIsOverflow(true);
} else if (isOverflow && textWidth < parseInt(width)) {
setIsOverflow(false);
}
}
},
[width]
);
const textRender = () => {
return (
<span
ref={textRef}
style={isOverflow ? textStyles : { display: 'inline-block' }}
{...restProps}
>
{children}
</span>
);
};
return (
<div style={{ width }}>
{isOverflow ? (
<Popover content={<pre className={styles.pop}>{children}</pre>}>{textRender()}</Popover>
) : (
textRender()
)}
</div>
);
}
關鍵點:
- span的樣式,要設置爲inline-block,方便取到文本寬度。
- 文本可能會更新,所以需要監聽children對象,變化時更新文本寬度、是否超長。
- 組件寬度是根據props參數動態適應,所以也需要監聽,變化時要更新是否超長的狀態。
總結
第二次使用React Hooks,確確實實感受到了好處。
-
userEffect的依賴設置非常靈活好用。
- 不設置,每次更新都會觸發。
- 設置爲空,只在第一次加載時觸發。
- 設置爲其他狀態、或props中的狀態時,只在這些狀態變化時觸發(*注意,依賴爲對象時,不會深比較)。
- 得益於useState, useEffect的用法靈活,Hooks組件寫法上確實簡潔不少。