它会卸载,因为该 node 不再存在于虚拟DOM中.你无法阻止这一点.
但是...
你可以弄些小把戏让它发挥作用
You can hide it via css个
您可以通过display: none
有条件地隐藏组件,而不是有条件地呈现组件.它将挂载组件两次,但如果条件改变,它不会在重现器之间卸载
Use common parent个
如果元素在渲染器之间有相同的父元素,它将不会卸载.
if (condition) {
return <ComponentA><Widget key="widget"/></ComponentA>;
}
return (
<ComponentA>
<ComponentC />
<Widget key="widget" />
</ComponentA>
);
<Widget key="widget" />
key是必要的.因为来自ComponentA
的子元素会编译成Reaction元素数组,所以使用key
可以防止在元素顺序改变时卸载单个元素.在第一个条件Widget
中是第一个元素,在第二个条件中是第二个元素
Use Portal个
您可以将Widget附加到虚拟DOM的同一 node ,并使用portals
呈现在您想要的任何位置
import { createPortal } from 'react-dom'
const Example = () => {
const [s, ss] = React.useState(true)
return (
<>
<button onClick={() => ss((o) => !o)}>click</button>
<Foo condition={s} />
</>
)
}
const Widget = () => {
React.useEffect(() => {
console.log('Widget mount')
}, [])
return <>widget</>
}
const ComponentA = ({ children }: { children: React.ReactElement }) => {
React.useEffect(() => {
console.log('ComponentA mount')
}, [])
return <>{children} inside component a</>
}
const ComponentC = ({ children }: { children: React.ReactElement }) => {
React.useEffect(() => {
console.log('ComponentC mount')
}, [])
return <>{children} inside component c</>
}
const Foo = ({ condition }: { condition: boolean }) => {
const ref = React.useRef<any>()
let content
if (condition) {
content = (
<ComponentA>
<div ref={ref} />
</ComponentA>
)
} else {
content = (
<ComponentC>
<div ref={ref} />
</ComponentC>
)
}
return (
<>
{content}
<Portal
dependency={[condition]}
elementRef={ref}
>
<Widget />
</Portal>
</>
)
}
const Portal = ({ elementRef, children, dependency = [] }: any) => {
const el = React.useRef<any>()
if (!el.current) {
el.current = document.createElement('div')
}
React.useLayoutEffect(() => {
const element = elementRef.current
element.appendChild(el.current)
return () => {
element.removeChild(el.current)
}
}, dependency) // we need to execute this again if the content changes
return createPortal(children, el.current)
}
在此示例中,<Widget />
:
- 是
Virtual DOM
名中content
名的sibling 姐妹
- 是
real DOM
岁的content
岁的子元素
在本例中,如果您单击按钮,它将更改内容,但小部件不会卸载