当前位置:首页 > 文章列表 > 文章 > 前端 > 在 Nextjs 中构建自动货币切换器

在 Nextjs 中构建自动货币切换器

来源:dev.to 2025-01-16 13:55:03 0浏览 收藏

欢迎各位小伙伴来到golang学习网,相聚于此都是缘哈哈哈!今天我给大家带来《在 Nextjs 中构建自动货币切换器》,这篇文章主要讲到等等知识,如果你对文章相关的知识非常感兴趣或者正在自学,都可以关注我,我会持续更新相关文章!当然,有什么建议也欢迎在评论留言提出!一起学习!

先决条件
在开始之前,请确保您对 next.js 和 react 有基本的了解。

1. 创建后端api路由

我们将创建一个与我们的 geolocation api 交互的 next.js api 路由。
在以下位置创建一个新文件:src/app/api/geolocation/route.ts

import { nextresponse } from "next/server";
import axios from "axios";

type ipgeolocation = {
    ip: string;
    version?: string;
    city?: string;
    region?: string;
    region_code?: string;
    country_code?: string;
    country_code_iso3?: string;
    country_fifa_code?: string;
    country_fips_code?: string;
    country_name?: string;
    country_capital?: string;
    country_tld?: string;
    country_emoji?: string;
    continent_code?: string;
    in_eu: boolean;
    land_locked: boolean;
    postal?: string;
    latitude?: number;
    longitude?: number;
    timezone?: string;
    utc_offset?: string;
    country_calling_code?: string;
    currency?: string;
    currency_name?: string;
    languages?: string;
    country_area?: number;
    asn?: string; // append ?fields=asn to the url
    isp?: string; // append ?fields=isp to the url
}

type ipgeolocationerror = {
  code: string;
  error: string;
}

export async function get() {
  // retrieve ip address using the getclientip function
  // for testing purposes, we'll use a fixed ip address
  // const clientip = getclientip(req.headers);

  const clientip = "84.17.50.173";

  if (!clientip) {
    return nextresponse.json(
      { error: "unable to determine ip address" },
      { status: 400 }
    );
  }

  const key = process.env.ipflare_api_key;

  if (!key) {
    return nextresponse.json(
      { error: "ipflare api key is not set" },
      { status: 500 }
    );
  }

  try {
    const response = await axios.get<ipgeolocation | ipgeolocationerror>(
      `https://api.ipflare.io/${clientip}`,
      {
        headers: {
          "x-api-key": key,
        },
      }
    );

    if ("error" in response.data) {
      return nextresponse.json({ error: response.data.error }, { status: 400 });
    }

    return nextresponse.json(response.data);
  } catch {
    return nextresponse.json(
      { error: "internal server error" },
      { status: 500 }
    );
  }
}

2. 获取您的api密钥

我们将使用名为 ip flare 的免费地理定位服务。访问 api 密钥页面:导航至 api 密钥页面。

访问:www.ipflare.io

从 api 密钥页面我们可以获得 api 密钥,并且可以使用快速复制将其作为环境变量存储在 .env 文件中。我们将用它来验证我们的请求。
image description

3. 创建前端组件

我创建了这个一体化组件,其中包括提供程序和货币选择器。我正在使用 shadcn/ui 和一些我在网上找到的标志 svg。

您需要将应用程序包装在 中,以便我们可以访问上下文。

现在,在应用程序中任何想要访问货币的地方,我们都可以使用钩子 const {currency } = usecurrency();。

要将其与 stripe 集成,当您创建结帐时,您只需发送货币并确保已将多货币定价添加到您的 stripe 产品中。

"use client";

import { useRouter } from "next/navigation";
import {
  createContext,
  type FC,
  type ReactNode,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";
import axios from "axios"; // 1) Import axios
import { Flag } from "~/components/flag";
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "~/components/ui/select";
import { cn } from "~/lib/utils";
import { type Currency } from "~/server/schemas/currency";

// -- [1] Create a local type for the data returned by /api/geolocation.
type GeolocationData = {
  country_code?: string;
  continent_code?: string;
  currency?: string;
};

type CurrencyContext = {
  currency: Currency;
  setCurrency: (currency: Currency) => void;
};

const CurrencyContext = createContext<CurrencyContext | null>(null);

export function useCurrency() {
  const context = useContext(CurrencyContext);
  if (!context) {
    throw new Error("useCurrency must be used within a CurrencyProvider.");
  }
  return context;
}

export const CurrencyProvider: FC<{ children: ReactNode }> = ({ children }) => {
  const router = useRouter();

  // -- [2] Local state for geolocation data
  const [location, setLocation] = useState<GeolocationData | null>(null);
  const [isLoading, setIsLoading] = useState<boolean>(true);

  // -- [3] Fetch location once when the component mounts
  useEffect(() => {
    const fetchLocation = async () => {
      setIsLoading(true);
      try {
        const response = await axios.get("/api/geolocation");
        setLocation(response.data);
      } catch (error) {
        console.error(error);
      } finally {
        setIsLoading(false);
      }
    };

    void fetchLocation();
  }, []);

  // -- [4] Extract currency from location if present (fallback to "usd")
  const geoCurrency = location?.currency;

  const getInitialCurrency = (): Currency => {
    if (typeof window !== "undefined") {
      const cookie = document.cookie
        .split("; ")
        .find((row) => row.startsWith("currency="));
      if (cookie) {
        const value = cookie.split("=")[1];
        if (value === "usd" || value === "eur" || value === "gbp") {
          return value;
        }
      }
    }
    return "usd";
  };

  const [currency, setCurrencyState] = useState<Currency>(getInitialCurrency);

  useEffect(() => {
    if (!isLoading && geoCurrency !== undefined) {
      const validatedCurrency = validateCurrency(geoCurrency, location);
      if (validatedCurrency) {
        setCurrency(validatedCurrency);
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isLoading, location, geoCurrency]);

  // -- [5] Update currency & store cookie; no more tRPC invalidation
  const setCurrency = (newCurrency: Currency) => {
    setCurrencyState(newCurrency);
    if (typeof window !== "undefined") {
      document.cookie = `currency=${newCurrency}; path=/; max-age=${
        60 * 60 * 24 * 365
      }`; // Expires in 1 year
    }
    // Removed tRPC invalidate since we are no longer using tRPC
    router.refresh(); 
  };

  const contextValue = useMemo<CurrencyContext>(
    () => ({
      currency,
      setCurrency,
    }),
    [currency],
  );

  return (
    <CurrencyContext.Provider value={contextValue}>
      {children}
    </CurrencyContext.Provider>
  );
};

export const CurrencySelect = ({ className }: { className?: string }) => {
  const { currency, setCurrency } = useCurrency();
  return (
    <Select value={currency} onValueChange={setCurrency}>
      <SelectTrigger className={cn("w-[250px]", className)}>
        <SelectValue placeholder="Select a currency" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup className="text-sm">
          <SelectItem value="usd">
            <div className="flex items-center gap-3">
              <Flag code="US" className="h-4 w-4 rounded" /> <span>$ USD</span>
            </div>
          </SelectItem>
          <SelectItem value="eur">
            <div className="flex items-center gap-3">
              <Flag code="EU" className="h-4 w-4 rounded" /> <span>€ EUR</span>
            </div>
          </SelectItem>
          <SelectItem value="gbp">
            <div className="flex items-center gap-3">
              <Flag code="GB" className="h-4 w-4 rounded" /> <span>£ GBP</span>
            </div>
          </SelectItem>
        </SelectGroup>
      </SelectContent>
    </Select>
  );
};

// -- [6] Use our new GeolocationData type in place of RouterOutputs
const validateCurrency = (
  currency: string,
  location?: GeolocationData | null,
): Currency | null => {
  if (currency === "usd" || currency === "eur" || currency === "gbp") {
    return currency;
  }

  if (!location) {
    return null;
  }

  if (location.country_code === "GB") {
    return "gbp";
  }

  // Check if they are in the EU
  if (location.continent_code === "EU") {
    return "eur";
  }

  // North America
  if (location.continent_code === "NA") {
    return "usd";
  }

  return null;
};

以上就是《在 Nextjs 中构建自动货币切换器》的详细内容,更多关于的资料请关注golang学习网公众号!

版本声明
本文转载于:dev.to 如有侵犯,请联系study_golang@163.com删除
Linux内核调试方法Linux内核调试方法
上一篇
Linux内核调试方法
用 igt 赚钱
下一篇
用 igt 赚钱
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 茅茅虫AIGC检测:精准识别AI生成内容,保障学术诚信
    茅茅虫AIGC检测
    茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
    114次使用
  • 赛林匹克平台:科技赛事聚合,赋能AI、算力、量子计算创新
    赛林匹克平台(Challympics)
    探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
    131次使用
  • SEO  笔格AIPPT:AI智能PPT制作,免费生成,高效演示
    笔格AIPPT
    SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
    133次使用
  • 稿定PPT:在线AI演示设计,高效PPT制作工具
    稿定PPT
    告别PPT制作难题!稿定PPT提供海量模板、AI智能生成、在线协作,助您轻松制作专业演示文稿。职场办公、教育学习、企业服务全覆盖,降本增效,释放创意!
    121次使用
  • Suno苏诺中文版:AI音乐创作平台,人人都是音乐家
    Suno苏诺中文版
    探索Suno苏诺中文版,一款颠覆传统音乐创作的AI平台。无需专业技能,轻松创作个性化音乐。智能词曲生成、风格迁移、海量音效,释放您的音乐灵感!
    129次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码