自定义钩子返回值在组件中不会改变

发布于 2025-01-10 13:16:36 字数 1155 浏览 1 评论 0原文

我的自定义挂钩异步获取数据。当它在组件中使用时,返回值不会更新。它一直显示默认值。有人知道发生了什么事吗?谢谢你!

import React, {useState, useEffect} from 'react'
import { getDoc, getDocs, Query, DocumentReference, deleteDoc} from 'firebase/firestore'

export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
  const [value, setValue] = useState<T|undefined>(undefined)
  const [isLoading, setIsLoading] = useState<boolean>(true)
  const update = async () => {
    const docSnap = await getDoc(docRef)
    if (docSnap.exists()) {
      const data = docSnap.data()
      setValue(data)
    }
    setIsLoading(false)
  }
  useEffect(() => {
    update()
  }, [])
  console.log(value, isLoading)  // it can shows correct data after fetching
  return {value, isLoading}
}
import { useParams } from 'react-router-dom'
const MyComponent = () => {
  const {userId} = useParams()
  const docRef = doc(db, 'users', userId!)
  const {value, isLoading} = useFirestoreDocument(docRef)
  console.log(value, isLoading)  // keeps showing {undefined, true}.
  return (
    <div>
    ...
    </div>
  )
}

My custom hook fetches data asynchronously. When it is used in a component, returned value doesn't get updated. It keeps showing default value. Does anybody know what is going on? Thank you!

import React, {useState, useEffect} from 'react'
import { getDoc, getDocs, Query, DocumentReference, deleteDoc} from 'firebase/firestore'

export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
  const [value, setValue] = useState<T|undefined>(undefined)
  const [isLoading, setIsLoading] = useState<boolean>(true)
  const update = async () => {
    const docSnap = await getDoc(docRef)
    if (docSnap.exists()) {
      const data = docSnap.data()
      setValue(data)
    }
    setIsLoading(false)
  }
  useEffect(() => {
    update()
  }, [])
  console.log(value, isLoading)  // it can shows correct data after fetching
  return {value, isLoading}
}
import { useParams } from 'react-router-dom'
const MyComponent = () => {
  const {userId} = useParams()
  const docRef = doc(db, 'users', userId!)
  const {value, isLoading} = useFirestoreDocument(docRef)
  console.log(value, isLoading)  // keeps showing {undefined, true}.
  return (
    <div>
    ...
    </div>
  )
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

瞎闹 2025-01-17 13:16:36

看起来 youe hook 在渲染时只执行一次,因为它缺少 docRef 作为依赖项:

export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
  const [value, setValue] = useState<T|undefined>(undefined)
  const [isLoading, setIsLoading] = useState<boolean>(true)
 
  useEffect(() => {
    const update = async () => {
      const docSnap = await getDoc(docRef)
      if (docSnap.exists()) {
        const data = docSnap.data()
        setValue(data)
      }
      setIsLoading(false)
    }
    update()
  }, [docRef])
  console.log(value, isLoading)  // it can shows correct data after fetching
  return {value, isLoading}
}

另外:将您的 update 函数定义放在 useEffect 挂钩中,如果您在其他地方不需要它。否则,你的 linter 将会抱怨 exhaustive-deps 规则。

It looks like youe hook is only being executed once upon rendering, because it is missing the docRef as a dependency:

export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
  const [value, setValue] = useState<T|undefined>(undefined)
  const [isLoading, setIsLoading] = useState<boolean>(true)
 
  useEffect(() => {
    const update = async () => {
      const docSnap = await getDoc(docRef)
      if (docSnap.exists()) {
        const data = docSnap.data()
        setValue(data)
      }
      setIsLoading(false)
    }
    update()
  }, [docRef])
  console.log(value, isLoading)  // it can shows correct data after fetching
  return {value, isLoading}
}

In addition: put your update function definition inside the useEffect hook, if you do not need it anywhere else. Your linter will complaing about the exhaustive-deps rule otherwise.

不打扰别人 2025-01-17 13:16:36

useEffect 钩子缺少对 docRef 的依赖:

export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
  const [value, setValue] = useState<T|undefined>(undefined);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  
  useEffect(() => {
    const update = async () => {
      setIsLoading(true);
      try {
        const docSnap = await getDoc(docRef);
        if (docSnap.exists()) {
          const data = docSnap.data();
          setValue(data);
        }
      } catch(error) {
        // handle any errors, log, etc...
      }
      setIsLoading(false);
    };

    update();
  }, [docRef]);

  return { value, isLoading };
};

渲染循环问题是因为 docRefMyComponent< 中的每个渲染周期都被重新声明。 /代码>。您应该记住该值,以便将稳定的引用传递给 useFirestoreDocument 挂钩。

const MyComponent = () => {
  const {userId} = useParams();

  const docRef = useMemo(() => doc(db, 'users', userId!), [userId]);

  const {value, isLoading} = useFirestoreDocument(docRef);

  console.log(value, isLoading);
  return (
    <div>
    ...
    </div>
  );
};

The useEffect hook is missing a dependency on the docRef:

export const useFirestoreDocument = <T>(docRef: DocumentReference<T>) => {
  const [value, setValue] = useState<T|undefined>(undefined);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  
  useEffect(() => {
    const update = async () => {
      setIsLoading(true);
      try {
        const docSnap = await getDoc(docRef);
        if (docSnap.exists()) {
          const data = docSnap.data();
          setValue(data);
        }
      } catch(error) {
        // handle any errors, log, etc...
      }
      setIsLoading(false);
    };

    update();
  }, [docRef]);

  return { value, isLoading };
};

The render looping issue is because docRef is redeclared each render cycle in MyComponent. You should memoize this value so a stable reference is passed to the useFirestoreDocument hook.

const MyComponent = () => {
  const {userId} = useParams();

  const docRef = useMemo(() => doc(db, 'users', userId!), [userId]);

  const {value, isLoading} = useFirestoreDocument(docRef);

  console.log(value, isLoading);
  return (
    <div>
    ...
    </div>
  );
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文