получение ошибки: Неверный вызов ловушки. Хуки могут быть вызваны только внутри тела функции в сценарии реакции. - PullRequest
0 голосов
/ 09 июля 2020

Я работаю над проектом response typescript, когда я пытаюсь использовать функцию useEffect, я получаю эту ошибку Invalid hook call. Hooks can only be called inside of the body of a function, здесь я загрузил весь свой код, может ли кто-нибудь изучить его и помочь мне решить эту проблему проблема, любая помощь будет очень признательна.

import { Redirect, RouteComponentProps, withRouter } from "react-router";
import React, { useEffect, useState } from "react";
import { GameDataStore, GameDataStorePayload } from "../../Global/DataStore/GameDataStore";
import { UserData, UserDataStore } from "../../Global/DataStore/UserDataStore";
import Helmet from "react-helmet";
import { Dialog, DialogContent, Typography } from "@material-ui/core";
import { ContainerProgress } from "../../UI/ContainerProgress";
import { LoadingButton } from "../../UI/LoadingButton";
import { Support } from "./Components/Gameplay/Support";
import { GameChatFab } from "./Components/Chat/GameChatFab";
import { ChatSidebar } from "./Components/Chat/ChatSidebar";
import { GameInner } from "./Components/Gameplay/GameInner";
import { SocketDataStore, SocketDataStorePayload } from "../../Global/DataStore/SocketDataStore";
import moment from "moment";
import { getTrueRoundsToWin } from "../../Global/Utils/GameUtils";
import { ClientGameItem } from "../../Global/Platform/Contract";
import { PlayerJoinApproval } from "@Areas/Game/Components/Gameplay/PlayerJoinApproval";

interface IGameParams {
    id: string;
}

interface IGameState {
    socketData: SocketDataStorePayload;
    gameData: GameDataStorePayload;
    userData: UserData;
    restartLoading: boolean;
    restartDelayed: boolean;
    showSupport: boolean;
    chatDrawerOpen: boolean;
    isFirstTime: boolean;
    total: any;
}

class Game extends React.Component<RouteComponentProps<IGameParams>, IGameState>
{
    private supportDelayTimeout = 0;
    public interval;

    constructor(props: RouteComponentProps<IGameParams>) {
        super(props);

        this.state = {
            socketData: SocketDataStore.state,
            gameData: GameDataStore.state,
            userData: UserDataStore.state,
            restartLoading: false,
            restartDelayed: true,
            showSupport: false,
            chatDrawerOpen: true,
            isFirstTime: false,
            total: 0,
        };
    }

    public componentDidMount(): void {
        GameDataStore.hydrate(this.props.match.params.id);

        SocketDataStore.listen(data => this.setState({
            socketData: data
        }));

        GameDataStore.listen(data => this.setState({
            gameData: data
        }));

        UserDataStore.listen(data => this.setState({
            userData: data
        }));


        if (this.state.gameData.game?.started || this.state.gameData.game == null) {

            console.log("socket data");
            console.log(this.state.socketData);
            

            if(this.state.socketData.gamePayload?.chooserGuid == this.state.userData.playerGuid) {
                this.interval = setInterval(() => {
                    this.setState(({ total }) => {
                        return { total: total + 1 };
                    });
                    if (this.state.total == 20) {
                        this.getUpdate();
                        clearInterval(this.interval);
                    }
                }, 1000);
            }
        }

        useEffect(() => {
            if(this.state.socketData.gamePayload?.chooserGuid == this.state.userData.playerGuid) {
                this.interval = setInterval(() => {
                    this.setState(({ total }) => {
                        return { total: total + 1 };
                    });
                    if (this.state.total == 20) {
                        this.getUpdate();
                        clearInterval(this.interval);
                    }
                }, 1000);
            }   
        });

        
    }



    componentWillUnmount() {
        clearInterval(this.interval);
    }

    private getUpdate = () => {
        let game_id = this.state.socketData.gamePayload?.id;
        let chooserGuid = this.state.socketData.gamePayload?.chooserGuid;
        let all_players = this.state.socketData.gamePayload?.players;
        if(typeof all_players != undefined && all_players !=null) {
            let all_player_id = Object.keys(all_players);
            let filteredAry = all_player_id.filter(e => e !== this.state.userData.playerGuid);
            let target_item: any = filteredAry.find((_, i, ar) => Math.random() < 1 / (ar.length - i));
            if( (typeof game_id !=undefined && game_id!=null) && (typeof chooserGuid!=undefined && chooserGuid!=null)) {
                return GameDataStore.skipPlayer(game_id, target_item, chooserGuid);
            }
        } 
    }



    private getWinnerFromState(state: IGameState) {
        const {
            players,
            settings
        } = state.gameData.game ?? {};

        const playerGuids = Object.keys(players ?? {});
        const roundsToWin = getTrueRoundsToWin(state.gameData.game as ClientGameItem);
        const winnerGuid = playerGuids.find(pg => (players?.[pg].wins ?? 0) >= roundsToWin);
        return winnerGuid;
    }

    public componentDidUpdate(prevProps: Readonly<RouteComponentProps<IGameParams>>, prevState: Readonly<IGameState>, snapshot?: any): void {
        const hadWinner = this.getWinnerFromState(prevState);
        const hasWinner = this.getWinnerFromState(this.state);
        if (!hadWinner && hasWinner && this.supportDelayTimeout === 0) {
            this.supportDelayTimeout = window.setTimeout(() => {
                this.setState({
                    restartDelayed: true,
                    showSupport: true
                });

                setTimeout(() => this.setState({
                    restartDelayed: false
                }), 5000);

            }, 2000);
        }
    }

    private restartClick = (playerGuid: string) => {
        this.setState({
            restartLoading: true
        });

        GameDataStore.restart(playerGuid)
            .finally(() => this.setState({
                restartLoading: false
            }));
    };

    public render() {

        



        const {
            id,
        } = this.props.match.params;

        if (!id) {
            return <Redirect to={"/"} />;
        }

        const {
            dateCreated,
            ownerGuid,
            spectators,
            pendingPlayers,
            players,
            settings,
        } = this.state.gameData.game ?? {};

        if (!this.state.gameData.game || !this.state.gameData.loaded || !this.state.socketData.hasConnection) {
            return <ContainerProgress />;
        }

        const {
            playerGuid
        } = this.state.userData;

        const owner = players?.[ownerGuid ?? ""];
        const amInGame = playerGuid in (players ?? {});
        const amSpectating = playerGuid in { ...(spectators ?? {}), ...(pendingPlayers ?? {}) };
        const title = `${unescape(owner?.nickname ?? "")}'s game`;

        const playerGuids = Object.keys(players ?? {});
        const roundsToWin = getTrueRoundsToWin(this.state.gameData.game as ClientGameItem);
        const winnerGuid = playerGuids.find(pg => (players?.[pg].wins ?? 0) >= roundsToWin);
        const canChat = (amInGame || amSpectating) && moment(dateCreated).isAfter(moment(new Date(1589260798170)));


        return (
            <>
                <Helmet>
                    <title>{title}</title>
                </Helmet>
                <PlayerJoinApproval />
                Timer={this.state.total}
                <GameInner gameId={id} />
                {winnerGuid && (
                    <Dialog open={this.state.showSupport} onClose={() => this.setState({ showSupport: false })}>
                        <DialogContent style={{ padding: "2rem" }}>
                            <Typography variant={"h6"} style={{ textAlign: "center" }}>
                                Game over! {unescape(players?.[winnerGuid].nickname ?? "")} is the winner.
                            </Typography>

                            <Support />

                            {playerGuid === ownerGuid && (
                                <div style={{
                                    marginTop: "7rem",
                                    textAlign: "center"
                                }}>
                                    <LoadingButton loading={this.state.restartLoading || this.state.restartDelayed} variant={"contained"} color={"secondary"} onClick={() => this.restartClick(playerGuid)}>
                                        Restart this game?
                                    </LoadingButton>
                                </div>
                            )}
                        </DialogContent>
                    </Dialog>
                )}
                {canChat && (
                    <>
                        <GameChatFab showChat={amInGame || amSpectating} />
                        <ChatSidebar />
                    </>
                )}
            </>
        );
    }
};

export default withRouter(Game);

1 Ответ

1 голос
/ 09 июля 2020

Вы смешиваете вещи, у вас есть useEffect внутри компонента класса, где хуки предназначены только для функциональных компонентов .

Прочтите о Время действия для имитации поведения в компоненте класса.

В отличие от componentDidMount и componentDidUpdate, функция, переданная в useEffect, срабатывает после макета и рисования во время отложенного события. Это делает его подходящим для многих распространенных побочных эффектов, таких как настройка подписок и обработчиков событий, потому что большинство типов работы не должны блокировать браузер от обновления экрана.

Что-то вроде:

componentDidUpdate(prevProps, prevState, snapshot) => {
  if(prevState.socketData.gamePayload?.chooserGuid === prevState.userData.playerGuid) {
     ... }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...