当我使用 useContext 挂钩时,为什么包装在 forwardRef 中的功能组件会收到空引用?
Why do functional components wrapped in forwardRef receive null refs when I use the useContext hook?
我有一个 sidebarCart 组件,基本上是一个购物车组件,它被包裹在 forwardRef 钩子中并包含 useImperativeHandle 将一个函数传递给 App 的父级。在我引入 useContext 钩子之前一切正常,现在 sidebarCart 中的引用变为空,这是什么奇怪的行为?
我说的 refs 是 cartContainer 和 pageShdowCover
我发现当停止我的 nodejs 服务器时,这个问题就消失了。
这是 sidebarCart 组件。
import React ,{createRef,forwardRef,useImperativeHandle,useContext,useEffect}from 'react'
import CartItem from './CartItem'
import {MyContext} from '../../Context/ProductsProvider'
const SideBarCart=forwardRef((props,ref)=>{
const {setCart,cart} =useContext(MyContext)
const cartContainer = createRef()
const pageShdowCover = createRef()
const slideOut=e=>{
cartContainer.current.style.right='-400px'
pageShdowCover.current.style.opacity='0'
setTimeout(()=>pageShdowCover.current.style.display='none', 600);
}
useImperativeHandle(ref, () => ({
slideIn(){
pageShdowCover.current.style.display='block'
cartContainer.current.style.right='0'
pageShdowCover.current.style.opacity='1'
}
}),[pageShdowCover,cartContainer]);
return (
<div>
<div className="pageShdowCover" ref={pageShdowCover} ></div>
<div className="SideContainer cart" ref={cartContainer}>
<div className="cart__top">
<h1>Cart</h1>
<i className="far fa-times-circle Close" onClick={slideOut}></i>
<a onClick={slideOut}>x</a>
</div>
<div className="cart__body">
</div>
</div>
</div>
)
})
export default SideBarCart
我认为 useContext
挂钩将导致 children 重新呈现而没有 parent 重新呈现。引用在更改时不会触发重新渲染,因此从 parent 传递到 children 的引用可能已过时? (我认为这就是答案 - 我可能不正确)。
总结(对我有用的):为了解决我遇到的问题,只需将 ref 存储在 state 中以在 ref 更改时触发 re-renders。
===
说明
我的use-case是我有一个parent组件有4个children。其中一个 children 是 header,我希望兄弟姐妹能够 'inject' 控制它们何时使用 ReactDOM.createPortal
进行渲染
- Parent
- Header
- A
- B
- C
我发现如果我只使用 ref,当 ref 分配给 DOM 时 parent 不会重新渲染,因此组件 A
、B
, C
不会重新渲染,因此他们不知道新分配的 ref.current
值。
即以下 NOT 工作
const Parent = () => {
const ref = useRef()
return (
<div>
<Header ref={el => { ref.current = el } />
<C ref={ref} />
<B ref={ref} />
<C ref={ref} />
</div>
)
}
// A, B, C have the same signature
const A = forwardRef((props, ref) => {
return (
<div>
{createPortal(<SomeComponent />, ref.current)}
<OtherComponent />
</div>
)
})
我在 Whosebug 上的 THIS 答案中找到了解决方案。我来到这个问题是因为 A
、B
和 C
正在使用 useContext(...)
。以前对我来说,当在 Parent
中创建状态而不是使用上下文时,上面的代码正在工作 - 可能是因为 parent 正在重新渲染并留下陈旧的引用或其他东西(我认为它正在工作'accidentally')。即问题与上下文无关,而是与重新渲染的触发方式有关。
这段代码可以工作
const Parent = () => {
const [ref, setRef] = useState()
return (
<div>
<Header ref={el => { setRef(el) } />
<C ref={ref} />
<B ref={ref} />
<C ref={ref} />
</div>
)
}
// A, B, C have the same signature
const A = forwardRef((props, ref) => {
return (
<div>
{createPortal(<SomeComponent />, ref)}
<OtherComponent />
</div>
)
})
我有一个 sidebarCart 组件,基本上是一个购物车组件,它被包裹在 forwardRef 钩子中并包含 useImperativeHandle 将一个函数传递给 App 的父级。在我引入 useContext 钩子之前一切正常,现在 sidebarCart 中的引用变为空,这是什么奇怪的行为?
我说的 refs 是 cartContainer 和 pageShdowCover 我发现当停止我的 nodejs 服务器时,这个问题就消失了。
这是 sidebarCart 组件。
import React ,{createRef,forwardRef,useImperativeHandle,useContext,useEffect}from 'react'
import CartItem from './CartItem'
import {MyContext} from '../../Context/ProductsProvider'
const SideBarCart=forwardRef((props,ref)=>{
const {setCart,cart} =useContext(MyContext)
const cartContainer = createRef()
const pageShdowCover = createRef()
const slideOut=e=>{
cartContainer.current.style.right='-400px'
pageShdowCover.current.style.opacity='0'
setTimeout(()=>pageShdowCover.current.style.display='none', 600);
}
useImperativeHandle(ref, () => ({
slideIn(){
pageShdowCover.current.style.display='block'
cartContainer.current.style.right='0'
pageShdowCover.current.style.opacity='1'
}
}),[pageShdowCover,cartContainer]);
return (
<div>
<div className="pageShdowCover" ref={pageShdowCover} ></div>
<div className="SideContainer cart" ref={cartContainer}>
<div className="cart__top">
<h1>Cart</h1>
<i className="far fa-times-circle Close" onClick={slideOut}></i>
<a onClick={slideOut}>x</a>
</div>
<div className="cart__body">
</div>
</div>
</div>
)
})
export default SideBarCart
我认为 useContext
挂钩将导致 children 重新呈现而没有 parent 重新呈现。引用在更改时不会触发重新渲染,因此从 parent 传递到 children 的引用可能已过时? (我认为这就是答案 - 我可能不正确)。
总结(对我有用的):为了解决我遇到的问题,只需将 ref 存储在 state 中以在 ref 更改时触发 re-renders。
===
说明
我的use-case是我有一个parent组件有4个children。其中一个 children 是 header,我希望兄弟姐妹能够 'inject' 控制它们何时使用 ReactDOM.createPortal
- Parent
- Header
- A
- B
- C
我发现如果我只使用 ref,当 ref 分配给 DOM 时 parent 不会重新渲染,因此组件 A
、B
, C
不会重新渲染,因此他们不知道新分配的 ref.current
值。
即以下 NOT 工作
const Parent = () => {
const ref = useRef()
return (
<div>
<Header ref={el => { ref.current = el } />
<C ref={ref} />
<B ref={ref} />
<C ref={ref} />
</div>
)
}
// A, B, C have the same signature
const A = forwardRef((props, ref) => {
return (
<div>
{createPortal(<SomeComponent />, ref.current)}
<OtherComponent />
</div>
)
})
我在 Whosebug 上的 THIS 答案中找到了解决方案。我来到这个问题是因为 A
、B
和 C
正在使用 useContext(...)
。以前对我来说,当在 Parent
中创建状态而不是使用上下文时,上面的代码正在工作 - 可能是因为 parent 正在重新渲染并留下陈旧的引用或其他东西(我认为它正在工作'accidentally')。即问题与上下文无关,而是与重新渲染的触发方式有关。
这段代码可以工作
const Parent = () => {
const [ref, setRef] = useState()
return (
<div>
<Header ref={el => { setRef(el) } />
<C ref={ref} />
<B ref={ref} />
<C ref={ref} />
</div>
)
}
// A, B, C have the same signature
const A = forwardRef((props, ref) => {
return (
<div>
{createPortal(<SomeComponent />, ref)}
<OtherComponent />
</div>
)
})