提问人:DrewS 提问时间:11/9/2023 更新时间:11/9/2023 访问量:27
购物车组件未在下一个 JS 应用程序中将数据保存到 localstorage
Cart Component not saving data to localstorage in Next JS Application
问:
我正在尝试将购物车功能添加到我的 nextJS 应用程序中,这是一个投注单。用户可以选择赔率,数据存储在购物车/投注单中。我遇到的问题是,一旦选择数据,数据就不会保存到LocalStorage。因此,当我刷新页面或访问另一个页面时,购物车/投注单每次都会被清除。下面是组件每个部分的代码。谁能看到数据未存储到 LocalStorage 的明显原因?
betslip.tsx
import React, { useContext } from 'react';
import BetslipContext from './betslip-context';
import { XMarkIcon } from '@heroicons/react/24/outline';
import classNames from 'classnames';
const Betslip = () => {
const { betslip, removeOddsFromBetslip, updateStakeInBetslip } = useContext(BetslipContext);
const updateStake = (e, index) => {
// Parse the stake as a number before sending to updateStakeInBetslip
const stakeValue = parseFloat(e.target.value);
updateStakeInBetslip(index, stakeValue);
};
return (
<div className="col-span-full xl:col-span-4 sm:col-span-8 mb-5 shadow-lg border border-slate-200 bg-white">
<header className="px-5 py-4 border-b border-slate-100 bg-[#1a2c49] dark:border-slate-700">
<h2 className="font-bold text-slate-50 dark:text-slate-100 uppercase">Bet Slip</h2>
</header>
<ul className="list-none">
{betslip.map((odds, index) => (
<li key={`${odds.event_id}-${odds.bookmaker}`} className="p-4 border-b border-gray-300 relative">
<button
className="absolute top-5 right-4 p-1 bg-red-500 text-white rounded text-xs"
onClick={() => removeOddsFromBetslip(odds.event_id, odds.bookmaker, odds.selection, odds.type)}
>
<XMarkIcon className="w-3 h-3"/>
</button>
<div className="flex items-center justify-between">
<div className="w-2/3 flex items-center space-x-2">
<img src={odds.bookie_icon} alt={odds.bookmaker} style={{ width: '25px', height: '25px' }}/>
<span className="font-bold text-base text-sm w-40">{odds.selection}</span>
<span className="font-bold text-base align-top">{odds.line > 0 ? `+${odds.line}` : odds.line}</span>
</div>
<div className="w-1/3 text-center">
<span className="font-bold text-base">${odds.odds.toFixed(2)}</span>
</div>
</div>
<div className="ml-8 text-sm text-black flex flex-col">
<div>
<p className='text-xs'>{odds.type}</p>
<p className='text-xs'>Including Overtime</p>
</div>
<div className="text-sm text-black flex flex-col">
<div className="flex justify-between mt-2">
<div className="w-1/2 text-left">
<p className="font-semibold text-xs">{odds.event_name}</p>
<p className="text-xs mt-3">{odds.sport} - {odds.league}</p>
<p className="inline-block text-xs">Start: {odds.event_start}</p> <span className="mx-1">-</span> <p className="inline-block text-xs">{odds.event_date}</p>
</div>
<div className="w-1/2 text-right">
<input
type="number"
placeholder="Stake"
className="w-20 py-1 px-1 mt-2 border rounded-lg text-right"
id={`stake-input-${index}`}
name={`stake-${index}`}
onChange={e => updateStake(e, index)}
/>
<p className="text-right mt-2 font-semibold">Return: ${(odds.odds * (betslip[index].stake || 0)).toFixed(2)}</p>
</div>
</div>
</div>
</div>
</li>
))}
</ul>
<div className='bg-orange-100 p-3 font-semibold'>Total Stake: ${betslip.reduce((total, odds) => total + (+odds.stake || 0), 0).toFixed(2)}</div>
<div className='bg-emerald-50 p-3 font-semibold'>Potential Return: ${betslip.reduce((total, odds) => total + ((+odds.odds) * (+odds.stake || 0)), 0).toFixed(2)}</div>
</div>
);
};
export default Betslip;
betslip-context.tsx
import React, { useState, createContext, useCallback } from 'react';
// Define the shape of the odds data and the betslip item
export interface Odds {
event_date: any;
event_start: any;
league: any;
sport: any;
event_name: any;
line: Boolean;
bookie_icon: any;
event_id: string;
bookmaker: string;
selection: string;
type: string;
odds: number;
stake?: number;
// ... include all other properties related to odds
}
// Define the context type for better TypeScript integration
interface BetslipContextType {
betslip: Odds[];
addOddsToBetslip: (odds: Odds) => void;
removeOddsFromBetslip: (eventId: string, bookmaker: string, selection: string, type: string) => void;
updateStakeInBetslip: (index: number, stake: number) => void;
}
// Create the context with the initial default values
const BetslipContext = createContext<BetslipContextType>({
betslip: [],
addOddsToBetslip: () => {},
removeOddsFromBetslip: () => {},
updateStakeInBetslip: () => {},
});
// The provider component that will wrap your application's component tree
export const BetslipProvider: React.FC = ({ children }) => {
const [betslip, setBetslip] = useState<Odds[]>([]);
const addOddsToBetslip = useCallback((odds: Odds) => {
setBetslip((prevBetslip) => [...prevBetslip, { ...odds, stake: 0 }]);
}, []);
const removeOddsFromBetslip = useCallback((eventId: string, bookmaker: string, selection: string, type: string) => {
setBetslip((prevBetslip) =>
prevBetslip.filter(
(odds) =>
!(odds.event_id === eventId && odds.bookmaker === bookmaker && odds.selection === selection && odds.type === type)
)
);
}, []);
const updateStakeInBetslip = useCallback((index: number, stake: string) => {
const stakeNumber = parseFloat(stake) || 0; // Convert to number and handle non-numeric inputs
setBetslip((prevBetslip) => {
const newBetslip = [...prevBetslip];
newBetslip[index].stake = stakeNumber;
return newBetslip;
});
}, []);
// The context provider passes down the betslip array and functions to manipulate it
return (
<BetslipContext.Provider value={{ betslip, addOddsToBetslip, removeOddsFromBetslip, updateStakeInBetslip }}>
{children}
</BetslipContext.Provider>
);
};
export default BetslipContext;
betslip-provider.tsx
import React, { useState, useCallback, useMemo, useEffect } from 'react';
import BetslipContext, { Odds } from './betslip-context';
interface BetslipContextType {
betslip: Odds[];
addOddsToBetslip: (odds: Odds) => void;
removeOddsFromBetslip: (eventId: string, bookmaker: string, selection: string, type: string) => void;
updateStakeInBetslip: (index: number, stake: number) => void;
totalStake: number;
totalReturn: number;
}
const BetslipProvider: React.FC = ({ children }) => {
const [betslip, setBetslip] = useState<Odds[]>(() => {
try {
const localData = localStorage.getItem('betslip');
return localData ? JSON.parse(localData) : [];
} catch (error) {
console.error("Failed to load betslip from localStorage", error);
return [];
}
});
useEffect(() => {
try {
localStorage.setItem('betslip', JSON.stringify(betslip));
} catch (error) {
console.error("Failed to save betslip to localStorage", error);
}
}, [betslip]);
const addOddsToBetslip = useCallback((odds: Odds) => {
setBetslip(prevBetslip => [...prevBetslip, { ...odds, stake: 0 }]);
}, []);
const removeOddsFromBetslip = useCallback((eventId: string, bookmaker: string, selection: string, type: string) => {
setBetslip(prevBetslip =>
prevBetslip.filter(odds =>
!(odds.event_id === eventId && odds.bookmaker === bookmaker && odds.selection === selection && odds.type === type))
);
}, []);
const updateStakeInBetslip = useCallback((index: number, stake: number) => {
setBetslip(prevBetslip => {
const newBetslip = [...prevBetslip];
newBetslip[index].stake = stake;
return newBetslip;
});
}, []);
const totalStake = useMemo(() => betslip.reduce((total, odds) => total + (odds.stake || 0), 0), [betslip]);
const totalReturn = useMemo(() => betslip.reduce((total, odds) => total + (odds.stake || 0) * odds.odds, 0), [betslip]);
const contextValue: BetslipContextType = {
betslip,
addOddsToBetslip,
removeOddsFromBetslip,
updateStakeInBetslip,
totalStake,
totalReturn,
};
return (
<BetslipContext.Provider value={contextValue}>
{children}
</BetslipContext.Provider>
);
};
export default BetslipProvider;
布局.tsx
"use client"
import React from 'react';
import Header from '@/components/ui/header';
import { BetslipProvider } from 'components/ui/betslip/betslip-context.tsx'; // Adjust the import path as necessary
export default function DefaultLayout({ children }: { children: React.ReactNode }) {
return (
// Wrap the existing layout with the BetslipProvider
<BetslipProvider>
<div className="flex h-[100vh] overflow-hidden">
{/* Content area */}
<div className="flex flex-col flex-1 overflow-y-auto ">
{/* Site header */}
<Header />
<main className="grow [&>*:first-child]:scroll-mt-16">
{children}
</main>
</div>
</div>
</BetslipProvider>
);
}
答: 暂无答案
评论