自定义钩子不会触发组件重新渲染,尽管状态发生了变化

时间:2020-08-12 13:46:54

标签: javascript reactjs firebase react-hooks react-context

上下文:我有一个自定义钩子,用于获取Firestore文档。它监视文档更改,然后返回它和加载状态。我在另一个组件中使用了这个钩子。

问题::对钩子返回的状态值(loadingdocument)进行的更改不会触发使用钩子重新渲染并查看更新后的值的组件。

我的发现:其他自定义钩子可以正常工作并触发渲染,因此useDocument钩子一定存在问题。如果另一个挂钩触发了重新渲染,则loading才会最终更新。 loadingdocument的值确实可以在挂钩中按预期更新,但是仍然不会触发组件的重新渲染。

重要说明: 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
}

2 个答案:

答案 0 :(得分:1)

此代码段中几乎没有错误,主要是因为变量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);
};

答案 1 :(得分:0)

因此,问题在于依赖项auth?.currentUserauth本身会在状态更改时正确触发重新渲染。问题似乎是auth?.currentUser中的更改无法检测到。

我的假设是,这是因为auth被跟踪,并且即使auth被firebase库更新了,对currentUser的引用也没有改变。我最终将currentUser移到了自己的useState中,现在该组件可以正确地重新渲染了。