You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Hook执行异常:useEffect传空数组无返回值,移除则无限循环

Fixing Your useCurrentLocation Hook: Empty Dependency Array Issues & Infinite Loops

Let's walk through what's causing your problems and fix the hook properly.

First, Why You're Seeing These Issues

  • Infinite loops without a dependency array: Without specifying dependencies, useEffect runs every time your component re-renders. Since your fetchLocation function calls setLocation or setError, those state updates trigger a re-render, which kicks off useEffect again—creating an infinite cycle.
  • No location with an empty dependency array: While the empty array should run the effect only once on mount, your current error handling has redundant logic, and you aren't accounting for potential changes to the options parameter. Additionally, missing a check for component mount status can lead to state updates being ignored if the geolocation call finishes after the component unmounts.

Here's the Fixed Hook Code

type GeoOptions = PositionOptions;
type GeoPosition = GeolocationPosition;
type GeoError = GeolocationPositionError;

const useCurrentLocation = (options: GeoOptions | null) => {
  const [location, setLocation] = useState<GeoPosition | null>(null);
  const [error, setError] = useState<GeoError | null>(null);

  useEffect(() => {
    let isMounted = true; // Track if component is still active to avoid stale state updates

    const fetchLocation = async () => {
      // First check if browser supports geolocation
      if (!navigator.geolocation) {
        if (isMounted) {
          setError({
            code: 0,
            message: "Geolocation is not supported by your browser",
          } as GeoError);
        }
        return;
      }

      try {
        const position = await new Promise<GeoPosition>((resolve, reject) => {
          navigator.geolocation.getCurrentPosition(
            resolve,
            reject,
            options ?? {
              enableHighAccuracy: true,
              timeout: 15000,
              maximumAge: 10000,
            }
          );
        });

        if (isMounted) {
          setLocation(position);
          setError(null); // Clear any previous errors when we get a valid location
        }
      } catch (err) {
        if (isMounted) {
          setLocation(null);
          setError(err as GeoError);
        }
      }
    };

    fetchLocation();

    // Cleanup: Mark component as unmounted to prevent stale state updates
    return () => {
      isMounted = false;
    };
  }, [options]); // Add options as a dependency to react to configuration changes

  return { location, error };
};

Key Fixes Explained

  • Dependency array with options: Now the effect only runs when options changes (or on initial mount), stopping infinite loops while still reacting to configuration updates.
  • Mounted flag cleanup: The isMounted variable ensures we don't try to update state after the component has unmounted, which avoids console warnings and ensures state changes only apply when the component is active.
  • Simplified error handling: We moved all state updates into the try/catch block, removed redundant checks (like if (loc) since getCurrentPosition's success callback always passes a valid position), and added a check for browser geolocation support.
  • Cleaner default option handling: Used ?? (nullish coalescing) instead of a ternary for more readable default option logic.

How to Use It

Call the hook with your desired options (or null to use defaults):

const { location, error } = useCurrentLocation(null);

if (error) {
  return <div>Error: {error.message}</div>;
}

if (!location) {
  return <div>Loading location...</div>;
}

return (
  <div>
    Latitude: {location.coords.latitude}<br />
    Longitude: {location.coords.longitude}
  </div>
);

内容的提问来源于stack exchange,提问作者tvankith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 00:09:09