問題
Version: Ant Design 3.26.7
Issue:Ant-design Table組件當滾動列的行高變化時,固定列的行高不會同步變化,或固定列變化滾動列不變,造成佈局破壞。
本來以爲是文檔沒有看仔細,想尋求官方的配置方式來解決該問題。既然官方仍在處理中
,在這裏給出自己的解決方案,希望能夠幫到其他同學。
原理
首先,先解釋下 Table組件“固定頭和列” 的實現原理。我們所看到的一個Table組件,其本質是有三個Table元素組成,只不過被用特殊樣式的容器div(Wrapper div)包裹起來。如下所示
<div class="ant-table-content">
<div class="ant-table-scroll">...</div>
<div class="ant-table-fixed-left">...</div>
<div class="ant-table-fixed-right">...</div>
</div>
通過類名我們可以很直觀的瞭解,一個表被固定在左,一個固定在右,一個位於中間用於滾動。
在創建表的時候,我們通過Column
的fixed: true|left|right|false
屬性來決定該列屬於哪個容器,或者說 “指定這三個容器各自包含哪些列”。準確來說,應該是 指定left,right這兩個固定容器各自包含哪些列,因爲scroll容器包含了全部的列 ,這也是實現這種推拉效果的核心。
需要注意的是,ant-table-fixed-left
與ant-table-fixed-right
,由於被固定在左右,所以不能左右滾動,所以它們的包裝容器,被設置爲
<div style=“max-height: ${y}; overflow-y: scroll;”> </div>
僅實現上下滾動。
而ant-table-scroll
需要同時滿足左右上下滾動,所以被設置爲
<div style=“max-height: ${y}; overflow: scroll;”> </div>
其中這裏的y
,等於你所設置的Table組件的scroll.y
屬性。(個人見解,如果有誤請指出,先謝過)
分類
先看正常狀態
根據產生原因
可分爲兩類
- 滾動列發生變化,固定列不變,佈局被破壞。
- 固定列發生變化,滾動列和另一個固定列不變,佈局被破壞。
這裏我們以“右固定列發生變化”
爲例,進行問題解決,通過同樣的方式你也可以很容易得出其他情況的解決方式。
實例
先給出表的代碼
<Table id={tableId} rowKey={(t, index) => {
/**指定key,爲了在行tr高改變的時候,獲取其他容器中對應的行*/
t.key = `${t.name}-${index}`; return t.key;
}}
components={components}
rowClassName={() => 'editable-row'}
defaultExpandAllRows
scroll={{ y: 'max-content', x: 1300 }}
pagination={false} columns={columns} dataSource={dataSource} />
題
右固定列
發生變化,採用滾動列和左固定列伸縮
恢復佈局。
解
在組件更新(componentDidUpdate)時,獲取右固定容器發生變化的行tr
的真實高度(height),對滾動容器和左固定容器的對應行tr
的 style.height 進行相應改變。
const [rowKeyObject, setRowKeyObject] = useState({});
const onUp = (rowKey) => setRowKeyObject({ rowKey });
const didUpdateRef = useRef(false);
useEffect(() => {
if (!didUpdateRef.current) {
didUpdateRef.current = true;
return;
}
const { rowKey } = rowKeyObject
if (!rowKey)
return;
const scrollDiv = document.querySelector(`#${tableId} .ant-table-scroll > .ant-table-body`);
const leftFixedDiv = document.querySelector(`#${tableId} .ant-table-fixed-left .ant-table-body-inner`);
const rightFixedDiv = document.querySelector(`#${tableId} .ant-table-fixed-right .ant-table-body-inner`);
const cssSelector = `table.ant-table-fixed tr[data-row-key='${rowKey}']`;
const rightFixedTr = rightFixedDiv.querySelector(cssSelector);
const leftFixedTr = leftFixedDiv.querySelector(cssSelector);
const scrollTr = scrollDiv.querySelector(cssSelector);
const theoryTrHeight = getComputedStyle(rightFixedTr).height;
scrollTr.style.height = theoryTrHeight;
leftFixedTr.style.height = theoryTrHeight;
console.log(theoryTrHeight);
});
注意
useEffect()
在組件掛載(componentDidMount)和更新(componentDidUpdate)時都會被執行,顯然在掛載時我們不需要執行函數中的複雜邏輯,這裏使用易變對象ref
(mutable ref)來優化,ref.current
做狀態標記,掛載時不再執行復雜邏輯。- 這裏
id={tableId}
,是針對多表同時存在時使用的,tableId
作唯一標識,如果你的Document中只有一個Table組件存在,那麼你可以忽略該屬性。 - 爲了獲取變化的行tr,我們需要指定Table組件的rowKey屬性(rowKey對應了tr的
data-row-key
屬性),同時需要在tr高度發生變化時將rowKey值傳遞給useEffect()
,所以你需要保證你的EditableCell組件
能夠準確的拿到相應rowKey
值。在這裏,我將它綁定到了record.key
。 - 設置rowKey狀態時,沒有直接使用
而是使用const [rowKey, setRowKey] = useState(null);
原因是——如果某行tr高度持續變化,那麼傳遞到const [rowKeyObject, setRowKeyObject] = useState(null);
setRowKey(rowKey)
的rowKey
將是相等(===
)的值,React認爲State未發生變化,組件不會更新,useEffect()
不會被執行。而setRowKeyObject({rowKey})
,每次會創建一個新的State對象,其引用Reference,是不相等的(===
),組件會更新,useEffect()
會執行。 - 行tr高度但凡發生變化,你都需要促使組件更新,
useEffect()
執行。那麼你需要在EditableCell組件
中設置相應的監聽函數,例如onKeyUp
、onMouseUp
、onMouseOver
、onMouseEnter
等等。例如,return (<td {...restProps} onKeyUp={() => onUp(record.key)} onMouseUp={() => onUp(record.key)} > {editable ? this.renderCell() : children} </td>);
如果有錯誤的地方,請指出,先行謝過。
修訂:Ant Design v4已經發布,直接升級到v4,問題就全解決了
,《從 v3 到 v4》