尽管状态发生了变化,但自定义钩子不会触发组件重新渲染
Posted
技术标签:
【中文标题】尽管状态发生了变化,但自定义钩子不会触发组件重新渲染【英文标题】:Custom hook not triggering component rerender despite changes in state 【发布时间】:2020-12-02 06:48:38 【问题描述】:上下文:我有一个自定义挂钩来获取 Firestore 文档。它监视文档更改,然后返回它和加载状态。我在另一个组件中使用了这个钩子。
问题:更改挂钩返回的状态值(loading
和document
)不会触发组件使用挂钩重新呈现并查看更新的值。
我的发现:其他自定义钩子可以工作并触发渲染,所以useDocument
钩子肯定有问题。如果另一个钩子触发了重新渲染,那么loading
才会最终更新。 loading
和 document
的值确实在钩子中按预期更新,但组件的重新渲染仍然不会触发。
重要说明: useFirebase()
是在 useDocument
内调用的钩子,其状态使用 constate
库提升到上下文中。所以从技术上讲,这个钩子是一些提供者的消费者,它位于跟踪 firebase 库的树的上层。这是为了避免不必要的网络调用。
export function useDocument()
const auth, firestore = useFirebase() // asynchronously fetches firebase modules
const [loading, setLoading] = useState(true)
const [document, setDocument] = useState()
let unsubscribe = null
useEffect(() =>
let unsubscribe = null
const watchDocument = () =>
params?.query.onSnapshot(
(doc) =>
setDocument(doc.data() as T | undefined)
setLoading(false)
,
(err) =>
console.log(err)
setError(err.message)
)
if (firestore && auth?.currentUser)
unsubscribe = watchDocument()
return () =>
unsubscribe && unsubscribe()
, [firestore, auth?.currentUser])
return
document,
loading
const MyComponent = () =>
const document, loading = useDocument()
console.log(loading) // changes to "loading" doesn't trigger rerender of this component
【问题讨论】:
【参考方案1】:这个sn-p的错误很少,主要是变量unsubscribe
总是undefined
,因为它甚至不在useEffect
的范围内。
这可能是一个有效的修复(从未使用过 firebase,仅通过查看代码):
export function useDocument()
const auth, firestore = useFirebase();
const [loading, setLoading] = useState(true);
const [document, setDocument] = useState();
useEffect(() =>
const watchDocument = async () =>
const unsubscribe = await params?.query.onSnapshot(
(doc) =>
setDocument(doc.data());
setLoading(false);
,
(err) =>
console.log(err);
setError(err.message);
);
;
let unsubscribe = null;
if (firestore && auth)
unsubsribe = watchDocument();
return () =>
unsubscribe?.();
;
, [firestore, auth]);
return
document,
loading,
;
const MyComponent = () =>
const document, loading = useDocument();
console.log(loading);
;
【讨论】:
在深入挖掘并看到我确实在错误的地方定义了取消订阅后,我删除了我的原始评论。我用正确的代码更新了我的问题。谢谢!重新渲染行为最终成为一个参考问题。我还意识到我在问题中删除了太多代码,因此我对其进行了更新以反映问题所在。【参考方案2】:所以问题是依赖auth?.currentUser
。 auth
本身在状态更改时会正确触发重新渲染。问题似乎是无法检测到auth?.currentUser
中的更改。
我的假设是,这是因为 auth
是被跟踪的内容,即使 currentUser
由 firebase 库更新,对 auth
的引用也不会改变。我最终将currentUser
移动到了它自己的useState
中,现在组件正确地重新渲染了。
【讨论】:
以上是关于尽管状态发生了变化,但自定义钩子不会触发组件重新渲染的主要内容,如果未能解决你的问题,请参考以下文章
我的 Redux 状态发生了变化,为啥 React 没有触发重新渲染?