无法将我的游戏(井字游戏)的数据保存在本地存储中

问题描述 投票:0回答:1

我正在构建我的第一个下一个 JS 项目为了了解更多信息,所以我决定制作(井字棋游戏),如果有人当前刷新页面,我无法将游戏进度保存到浏览器本地存储将所有值设置为默认值,另外我还有另一个问题:是否也可以删除刷新页面后第一次显示的CSS?


import React, { useEffect, useRef, useState } from "react";
import { Button } from "react-bootstrap";
import Image from "next/image";
import Confetti from "../components/confetti";
import { Router, useRouter } from "next/router";

// Importing CSS files
import style from "@/styles/Home.module.css";

// Importing sound files
import clickSound from "@/public/sounds/click.mp3";
import winSound from "@/public/sounds/win.mp3";
import round1Sound from "@/public/sounds/round1.mp3";
import round2Sound from "@/public/sounds/round2.mp3";
import round3Sound from "@/public/sounds/round3.mp3";
import round4Sound from "@/public/sounds/round4.mp3";
import finalRoundSound from "@/public/sounds/finalRound.mp3";
// import backgroundSound from "@/public/sounds/background.mp3";
import youWinSound from "@/public/sounds/youwin.mp3";
import victorySound from "@/public/sounds/victory.mp3";

export default function Game(props) {
    const [count, setCount] = useState(0);
    const [boxes, setBox] = useState([...Array(9).fill({ symbol: "" })]);
    const [winnerNotFound, setWinnerNotFound] = useState(true);
    const [Player1Score, setPlayer1Score] = useState(0);
    const [Player2Score, setPlayer2Score] = useState(0);
    const [GoHome, setGoHome] = useState(false);
    const [round, setRound] = useState(1);
    const [WinnerName, setWinnerName] = useState("");
    const [loading, setLoading] = useState(true);
    const [playRound1SoundOnce, setPlayRound1SoundOnce] = useState(true);
    const [playRound2SoundOnce, setPlayRound2SoundOnce] = useState(true);
    const [playRound3SoundOnce, setPlayRound3SoundOnce] = useState(true);
    const [playRound4SoundOnce, setPlayRound4SoundOnce] = useState(true);
    const [finalRoundSoundOnce, setFinalRoundSoundOnce] = useState(true);
    const screenRef = useRef(null);
    const cancelRef = useRef(null);
    const confirmRef = useRef(null);
    const router = useRouter();

    // Distracting variables
    const { Player1, Player2 } = router.query;

    // This will create a array of reactor references to all the
    // button array that I will use to enable or disable button
    const buttonRef = useRef(
        Array.from({
            length: boxes.length,
        }).map(() => React.createRef())
    );
    const [screenText, setScreenText] = useState("");
    const [screenTextRenderFirstTime, setScreenTextRenderFirstTime] =
        useState(true);

    //===================================================
    // This is something good I learned
    const audioContextRef = useRef(null);

    useEffect(() => {
        window.addEventListener("beforeunload", confirmExit);
        // Create the audio context when the component mounts
        audioContextRef.current = new (window.AudioContext ||
            window.webkitAudioContext)();

        // Loading the saved game
        if (typeof window !== "undefined" && window.localStorage) {
            const savedGameState = window.localStorage.getItem("gameState");
            if (savedGameState) {
                const gameState = JSON.parse(savedGameState);
                // Set state variables based on the saved game state
                setCount(gameState.count);
                setBox(gameState.boxes);
                setWinnerNotFound(gameState.winnerNotFound);
                setPlayer1Score(gameState.Player1Score);
                setPlayer2Score(gameState.Player2Score);
                setRound(gameState.round);
                setWinnerName(gameState.WinnerName);
                setPlayRound1SoundOnce(gameState.playRound1SoundOnce);
                setPlayRound2SoundOnce(gameState.playRound2SoundOnce);
                setPlayRound3SoundOnce(gameState.playRound3SoundOnce);
                setPlayRound4SoundOnce(gameState.playRound4SoundOnce);
                setFinalRoundSoundOnce(gameState.finalRoundSoundOnce);
                setScreenText(gameState.screenText);
                setScreenTextRenderFirstTime(
                    gameState.screenTextRenderFirstTime
                );
                console.log("Saved Game State:", savedGameState);
                setLoading(false);
            } else {
                setLoading(false); // Mark loading as complete even if no data is found
            }
        } else {
            setLoading(false); // Mark loading as complete even if no data is found
        }

        return () => {
            // Clean up the audio context and event listener when the component unmounts
            window.removeEventListener("beforeunload", confirmExit);
            if (audioContextRef.current) {
                audioContextRef.current.close();
            }
        };
    }, []);

    const playAudio = (audioFile) => {
        if (!audioContextRef.current) {
            return;
        }

        // Fetch and decode the audio file
        fetch(audioFile)
            .then((response) => response.arrayBuffer())
            .then((audioData) => {
                audioContextRef.current.decodeAudioData(audioData, (buffer) => {
                    // Create an audio source and connect it to the context
                    const source = audioContextRef.current.createBufferSource();
                    source.buffer = buffer;
                    source.connect(audioContextRef.current.destination);

                    // Start playing the audio
                    source.start();
                });
            });
    };

    //===================================================

    useEffect(() => {
        checkWinner();
        checkWinCondition();
        controlScreen();
        if (GoHome) {
            router.push("/");
        }
    }, [boxes, Player1Score, Player2Score, GoHome]);

    // *********************logical for saving game*********************
    // Save game state to local storage whenever it changes
    useEffect(() => {
        // Save game state to local storage whenever it changes
        const gameState = {
            count: count,
            boxes: boxes,
            winnerNotFound: winnerNotFound,
            Player1Score: Player1Score,
            Player2Score: Player2Score,
            round: round,
            WinnerName: WinnerName,
            playRound1SoundOnce: playRound1SoundOnce,
            playRound2SoundOnce: playRound2SoundOnce,
            playRound3SoundOnce: playRound3SoundOnce,
            playRound4SoundOnce: playRound4SoundOnce,
            finalRoundSoundOnce: finalRoundSoundOnce,
            screenText: screenText,
            screenTextRenderFirstTime: screenTextRenderFirstTime,
        };
        if (typeof window !== "undefined" && window.localStorage) {
            window.localStorage.setItem("gameState", JSON.stringify(gameState));
        }
    }, [
        count,
        boxes,
        winnerNotFound,
        Player1Score,
        Player2Score,
        round,
        WinnerName,
        playRound1SoundOnce,
        playRound2SoundOnce,
        playRound3SoundOnce,
        playRound4SoundOnce,
        finalRoundSoundOnce,
        screenText,
        screenTextRenderFirstTime,
    ]);

    // *****************************************************************

    // This function is responsible for the following:
    // 1. it checks the winning pattern
    // 2. It the resets the count variable As well as all the buttons
    // 3. And increases the round number
    function checkWinner() {
        const winPatterns = [
            [0, 1, 2],
            [3, 4, 5],
            [6, 7, 8],
            [0, 3, 6],
            [1, 4, 7],
            [2, 5, 8],
            [0, 4, 8],
            [2, 4, 6],
        ];

        for (const pattern of winPatterns) {
            const [a, b, c] = pattern;
            if (
                boxes[a].symbol &&
                boxes[a].symbol === boxes[b].symbol &&
                boxes[a].symbol === boxes[c].symbol
            ) {
                // Increasing score when one player wins
                boxes[a].symbol === "X"
                    ? setPlayer1Score((prev) => prev + 1)
                    : setPlayer2Score((prev) => prev + 1);

                setRound((prev) => prev + 1);
                // This will automatically give a turn to X after each round
                setCount(0);
                // Resetting the values in buttons to empty
                boxes.forEach((box) => {
                    box.symbol = "";
                });
            }
        }
    }

    // This function checks the victory of a player as well as plays the Round and Win Sound
    function checkWinCondition() {
        if (Player1Score === 3 || Player2Score === 3) {
            Player1Score === 3
                ? setWinnerName(Player1)
                : setWinnerName(Player2);

            setWinnerNotFound(false);
            playAudio(victorySound);
            playAudio(youWinSound);
        } else {
            if (playRound1SoundOnce) {
                playAudio(round1Sound);
                setPlayRound1SoundOnce(false);
            } else if (round === 2 && playRound2SoundOnce) {
                playAudio(winSound);
                playAudio(round2Sound);
                setPlayRound2SoundOnce(false);
            } else if (round === 3 && playRound3SoundOnce) {
                playAudio(winSound);
                playAudio(round3Sound);
                setPlayRound3SoundOnce(false);
            } else if (round === 4 && playRound4SoundOnce) {
                playAudio(winSound);
                playAudio(round4Sound);
                setPlayRound4SoundOnce(false);
            } else if (round === 5 && finalRoundSoundOnce) {
                playAudio(winSound);
                playAudio(finalRoundSound);
                setFinalRoundSoundOnce(false);
            }
        }
    }

    // This function is responsible for the clicking sound as well as rendering the X and O
    function ChangeSymbol(index) {
        // clickAudio.play();
        playAudio(clickSound);
        if (!winnerNotFound) {
            return;
        }
        const updateBoxes = [...boxes];
        if (updateBoxes[index].symbol === "") {
            setCount((prev) => prev + 1);
            const symbol = (count) => {
                if (count % 2 === 0) {
                    return "X";
                } else {
                    return "O";
                }
            };
            updateBoxes[index] = {
                symbol: symbol(count),
            };
            setBox(updateBoxes);
        }
    }

    function rematch() {
        setWinnerNotFound(true);
        setCount(0);
        setRound(1);
        setPlayRound1SoundOnce(true);
        setPlayRound2SoundOnce(true);
        setPlayRound3SoundOnce(true);
        setPlayRound4SoundOnce(true);
        setFinalRoundSoundOnce(true);
        setWinnerName("");
        setPlayer1Score(0);
        setPlayer2Score(0);
        setScreenText("");
        setScreenTextRenderFirstTime(true);
    }

    function home() {
        rematch();
        setGoHome(true);
    }

    function homeInGame() {
        if (cancelRef && confirmRef) {
            cancelRef.current.style.visibility = "visible";
            confirmRef.current.style.visibility = "visible";
        }
    }
    function cancel() {
        if (cancelRef && confirmRef) {
            cancelRef.current.style.visibility = "hidden";
            confirmRef.current.style.visibility = "hidden";
        }
    }

    // This is the kind of the main part of the game where player
    // can see Who have the turn and it also displays the draw
    function controlScreen() {
        if (screenTextRenderFirstTime) {
            setScreenText(3);
            for (let i = 2; i > 0; i--) {
                setTimeout(() => {
                    setScreenText(i);
                }, 1000 * (2 - i + 1));
            }
            disableButtons(3);
            setScreenTextRenderFirstTime(false);
            setTimeout(() => {
                setScreenText("Go!");
            }, 3000);
        } else {
            if (count === 9) {
                setScreenText("It's Draw!");
                disableButtons(1.6);
                setTimeout(() => {
                    if (boxes) {
                        // Check if boxes is defined
                        const updatedBoxes = boxes.map((box) => ({
                            symbol: "",
                        }));
                        setBox(updatedBoxes); // Update the state to trigger a re-render
                    }
                }, 1500);
                setCount(0);
            } else if (count % 2 === 0) {
                setScreenText("X's Turn");
            } else if (count % 2 !== 0) {
                setScreenText("O's Turn");
            }
        }
    }

    // This is the reusable function to enable and disable the buttons for dynamic Times
    function disableButtons(seconds) {
        buttonRef.current.forEach((button) => {
            if (button.current) {
                button.current.setAttribute("disabled", true);
            }
        });
        setTimeout(() => {
            buttonRef.current.forEach((button) => {
                if (button.current) {
                    button.current.removeAttribute("disabled");
                }
            });
        }, seconds * 1000);
    }

    // Dissemination of well show the confirmation
    // message to the user before they reload the page
    const confirmExit = (event) => {
        event.preventDefault();
        // Customize the confirmation message
        event.returnValue =
            "Are you sure you want to leave? Your game progress will be lost.";
    };

    return (
        <>
            {winnerNotFound ? (
                <>
                    <div className={"mainContainerForScoreBoard"}>
                        <div className={"playerContainer1"}>
                            <span className={style.Player1}>{Player1}[X]</span>
                            <span className={"Player1Score"}>
                                {Player1Score}
                            </span>
                        </div>
                        <div ref={screenRef} className={"Screen"}>
                            {screenText}
                        </div>

                        <div className={"playerContainer2"}>
                            <span className={style.Player2}>{Player2}[O]</span>
                            <span className={"Player2Score"}>
                                {Player2Score}
                            </span>
                        </div>
                    </div>

                    <div className={"container"}>
                        {boxes.map((box, index) => (
                            <React.Fragment key={index}>
                                {index === 3 || index === 6 ? <br /> : null}
                                <button
                                    className={`${"box"} ${"fadeStyle"}`}
                                    onClick={() => ChangeSymbol(index)}
                                    ref={buttonRef.current[index]}
                                >
                                    {box.symbol}&nbsp;
                                </button>
                            </React.Fragment>
                        ))}
                    </div>

                    <Image
                        src="/images/vs.png"
                        width={300}
                        height={300}
                        className={style.vs}
                        alt="Versus image"
                        priority={true}
                    />
                    <div className={style.SingleHomeButton}>
                        <Button
                            ref={cancelRef}
                            onClick={() => cancel()}
                            className={style.cancel}
                        >
                            ❌
                        </Button>

                        <Button
                            variant="outline-primary"
                            onClick={() => homeInGame()}
                            className={style.SingleHomeButtonCSS}
                        >
                            Home
                        </Button>

                        <Button
                            ref={confirmRef}
                            onClick={() => home()}
                            className={style.confirm}
                        >
                            ✔️
                        </Button>
                    </div>
                </>
            ) : (
                <>
                    <Confetti />
                    <div className={style.winnerContainer}>
                        <p>winner</p>
                        <Image
                            src="/images/chain.png"
                            width={200}
                            height={200}
                            className="chain"
                            alt="Chain image"
                        />
                        <p className="winnerDesign">{WinnerName}</p>
                        {/* <p className="winnerDesign">Sukhvir</p> */}
                    </div>
                    <div className={style.BSbuttons}>
                        <Button
                            variant="outline-primary"
                            onClick={() => home()}
                            className={style.BSHome}
                        >
                            Home
                        </Button>
                        <Button
                            variant="outline-primary"
                            onClick={() => rematch()}
                            className={style.BSRematch}
                        >
                            Rematch
                        </Button>
                    </div>
                </>
            )}
        </>
    );
}

如果我尝试控制台 localStorage 中保存的数据,我可以看到它,但重新加载后所有内容都会设置为默认值

next.js local-storage
1个回答
0
投票

我只是简单地检查了您的代码,但似乎在加载数据的 useEffect 之前调用了将数据保存到本地存储的 useEffect - 从而重置了数据。将一些控制台日志放在那里来跟踪订单。

此外,您保存和恢复数据的方式很好并且应该有效。

© www.soinside.com 2019 - 2024. All rights reserved.