我們正在用 React 組件包裝一個組件庫,但在某些情況下,該庫對 DOM 樹的操作方式導致 React 在試圖移除 React 組件時崩潰。
下面是一個再現該問題的示例:
function Sample ( )
{
let [shouldRender, setShouldRender] = React.useState(true) 。
return (
<React.Fragment>
<button onClick={() => setShouldRender(! shouldRender)}>顯示/隱藏</按鈕>
{ shouldRender && <Component />/span> }
</React.Fragment>
);
}
function Component ( )
{
let ref = React.useRef()。
React.useEffect(() => {
let divElement = ref.current;
someExternalLibrary.setup(divElement)。
return () => someExternalLibrary.cleanup(divElement)。
});
return < div ref={ref} id="div1"> Hello world</div> 。
}
ReactDOM.render(
<Sample />。
document.getElementById('container'/span>)
);
let someExternalLibrary = {
setup: function(divElement)
{
let beacon = document.createElement('div')。
beacon.id = `beacon${divElement.id}`。
divElement.parentElement.replaceChild(beacon, divElement)。
document.body.append(divElement)。
},
cleanup: function(divElement)
{
let beacon = document. getElementById(`beacon${divElement.id}`) 。
beacon.parentElement.replaceChild(divElement, beacon)。
}
你可以在JSFiddle上找到這個樣本。
。上面的示例將渲染Component,它與someExternalLibrary集成。
外部庫將React組件內部的元素移動到其他地方。
即使外部庫使用信標將元素放回原來的位置,當你點擊顯示/隱藏按鈕時,React仍然會在試圖移除該組件時抱怨。
這將是一個錯誤
。"錯誤。在'Node'上執行'removeChild'失敗了。要洗掉的節點不是這個節點的子節點。
at removeChildFromContainer (https://unpkg.com/react-dom@17/umd/react-dom.development.js:10337:17)
at unmountHostComponents (https://unpkg.com/react-dom@17/umd/react-dom.development.js:21324:11)
at commitDeletion (https://unpkg.com/react-dom@17/umd/react-dom.development.js:21377:7)
在 commitMutationEffects (https://unpkg.com/react-dom@17/umd/react-dom.development.js:23437:13)
at HTMLUnknownElement.callCallback (https://unpkg.com/react-dom@17/umd/react-dom.development.js:3942:16)
at Object.invokeGuardedCallbackDev (https://unpkg.com/react-dom@17/umd/react-dom.development.js:3991:18)
在 invokeGuardedCallback (https://unpkg.com/react-dom@17/umd/react-dom.development.js:4053:33)
at commitRootImpl (https://unpkg.com/react-dom@17/umd/react-dom.development.js:23151:11)
at unstable_runWithPriority (https://unpkg.com/react@17/umd/react.development.js:2764:14)
at runWithPriority$1 (https://unpkg.com/react-dom@17/umd/react-dom.development.js:11306:12)"/span>
一個簡單的修復方法是將現有的 HTML 包裹在另一個 DIV 元素中,這樣它就成為了組件的根,但不幸的是,這在我們的專案中并不總是可行的,所以我需要另一種解決方案。
什么是解決這個問題的最佳方法? 是否有辦法使用 ReactFragment 并在清理期間將 HTMLElement 與該片段重新關聯?
uj5u.com熱心網友回復:
useEffect的清理函式,即someExternalLibrary.cleanup(),是在React更新DOM(從DOM中移除div)之后被呼叫的。它失敗了,因為 React 正試圖移除 someExternalLibrary.setup() 所移除的 DOM 節點(而 someExternalLibrary.cleanup() 將在以后放回)。
在類組件中,你可以呼叫someExternalLibrary.cleanup() 在 React更新DOM之前。這將修復你的代碼:
class Component extendsReact.Component {
constructor(props) {
super(props)。
this.divElementRef = React.createRef()。
}
componentDidMount(){
someExternalLibrary.setup(this.divElementRef.current) 。
}
componentWillUnmount(){
someExternalLibrary.cleanup(this.divElementRef.current) 。
}
render() {
return <div ref={this. divElementRef} id="div1">Hello world</div> ;
}
}
"額外的div解決方案 "由于同樣的原因而失敗。在清理useEffect()的程序中呼叫someExternalLibrary.cleanup()將意味著DOM已經改變,但是someExternalLibrary期望DOM沒有改變。通過使用帶有componentWillUnmount的類組件,someExternalLibrary.setup()和someExternalLibrary.cleanup()將在同一個DOM下作業。
uj5u.com熱心網友回復:
React并不期望你洗掉React創建的節點。將React創建的節點視為只讀。相反,你應該:
將現有的HTML包裹在另一個DIV元素中。
但是你沒有解釋為什么這不可行:
但不幸的是,這在我們的專案中并不總是可行的,所以我需要另一種解決方案。
如果沒有這些資訊,也就是你真正的問題,就無法給出解決方案。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/321057.html
標籤:
