简体   繁体   中英

React Redux state not changing after calling useDispatch()

I'm trying to use the newer version of react redux v8.0.2 within my web-based game to keep state within my application without having to pass states between navigation calls.

Unfortunately I'm running into an issue where the player state gets initialized for the first time, but the final state is not being updated after calling the useDispatch() method. I've tried looking everywhere online, but none of the solutions that are currently out there have actually solved my issue.

I even force my functional component to rerender, and that still just returns the initial state of my player instead of the updated one that I'm expecting. Can anyone help me figure out what I'm missing here. It's got to be something small that isn't talked about on the redux forms/docs. TIA!

playerSlice.js

import {createSlice} from '@reduxjs/toolkit';

export const playerSlice = createSlice({
    name: 'player',
    initialState: {
        address: '',
        cp: 0,
        created: '',
        faction: 0,
        faction_selected: false,
        games_lost: 0,
        games_won: 0,
        online: false,
        selected_char: 0,
        selected: {
            combatType: '',
            lvl: 0,
            mgc: 0,
            str: 0,
            rng: 0,
            def: 0
        },
        time_played: 0,
        tokens: 10000,
        total_cp: 0,
        total_earned: 0,
        user_name: ""
    },
    reducers: {
        setInit: (state,action) => {
            state = action.payload;
        },
        setCP: (state,action) => {
            state.cp += action.payload;
        },
        setFaction: (state,action) => {
            state.faction = action.payload;
        },
        setGamesLost: (state,action) => {
            state.games_lost = action.payload;
        },
        setGamesWon: (state,action) => {
            state.games_won = action.payload;
        },
        setPlayerState: (state,action) => {
            state = {
                ...state,
                ...action.payload
            }
        }
    }
});

export const {setInit, setCP, setFaction, setGamesLost, setGamesWon, setPlayerState} = playerSlice.actions;

export const selectPlayer = (state) => state.player;

export default playerSlice.reducer;

index.js

import {configureStore} from '@reduxjs/toolkit';
import playerReducer from '../store/playerSlice';

export default configureStore({
    reducer: {
        player: playerReducer,
    },
})

Selection.js

import Card from './Card';
import React, {useState, useEffect} from 'react';
import '../stylesheet/Selection.css';
import Logo from '../assets/degen age title GNW skull.png';
import KnightTitle from '../assets/knights title.png';
import GoblinTitle from '../assets/goblins title.png';
import WizardTitle from '../assets/wizards title.png';
import ElfTitle from '../assets/elves title.png';
import SorcererShield from '../assets/sorcerers shield item.jpg';
import Weaken from '../assets/weaken item img.jpg';
import Barrage from '../assets/barrage item img.jpg';
import Berserk from '../assets/berserk item img.jpg';
import {db} from '../firebase/firestore';
import {addDoc,collection, serverTimestamp} from 'firebase/firestore';
import { useNavigate, useLocation } from 'react-router-dom';
import {CHAR_RACES} from '../constants';
import {useSelector, useDispatch} from 'react-redux';
import {setInit, selectPlayer} from '../store/playerSlice';

const SCREEN_DELAY = 4000; // delay in ms

const Selection = () => {
    const [initScreen, setInitScreen] = useState(true);
    const player = useSelector(selectPlayer);  
    const dispatch = useDispatch();
    const navigate = useNavigate();
    const [ready,setReady] = useState(false);
    const {state} = useLocation();

    useEffect(() => {
        let mounted = true;

        if(mounted){
            setTimeout(() => {
                setInitScreen(false);
            },SCREEN_DELAY);
        }

        return () => {
            mounted = false;
        }
    },[]);

    const handleFactionSelect = async (_faction) => {
        // add new player to db
        const ref = collection(db, 'players');
        const playerData = {
            address: state.address,
            cp: 0,
            created: serverTimestamp(),
            faction: _faction,
            faction_selected: true,
            games_lost: 0,
            games_won: 0,
            online: true,
            selected_char: 0,
            selected: {
                combatType: 'MELEE',
                lvl: 200,
                mgc: 10,
                str: 59,
                rng: 30,
                def: 101
            },
            time_played: 0,
            tokens: 10000,
            total_cp: 0,
            total_earned: 0,
            user_name: "someUser393900"
        }

        dispatch(setInit({
            ...playerData,
            created: new Date().getTime(),
            faction: _faction
        }))

        console.log({player});
        // set in redux as well****
        // addDoc(ref,playerData).then(res => {
        //     if(res.id){
        //         const _faction = CHAR_RACES[playerData.faction];
        //         dispatch(setInit({
        //             ...playerData,
        //             created: new Date().getTime(),
        //             faction: _faction
        //         }))
        //         // navigate('/play',{
        //         //     state: {
        //         //         player: {
        //         //             ...playerData,
        //         //             faction: _faction
        //         //         }
        //         //     }
        //         // });
        //         navigate('/play');
        //     }
        // }).catch(error => {
        //     console.error(error);
        // })
    }

    return (
        <div className='select-main'>
            {!initScreen ? <div id="main-select" className='fade-in-slow2 select-wrapper'>
                <h1 className='text-center'>CHOOSE YOUR SIDE</h1>
                <div className='select-cards'>
                    <Card cardStyle="f1" ability={SorcererShield} desc="Sorcerers Shield" title={WizardTitle} name={1} onClick={handleFactionSelect} />
                    <Card cardStyle="f3" ability={Berserk} desc="Berserk" title={KnightTitle} name={2} onClick={handleFactionSelect} />
                    <Card cardStyle="f2" ability={Barrage} desc="Barrage" title={ElfTitle} name={0} onClick={handleFactionSelect} />
                    <Card cardStyle="f4" ability={Weaken} desc="Weaken" title={GoblinTitle} name={3} onClick={handleFactionSelect} />
                </div>
            </div> :
            <div className='fade-in-slow sub-select-wrapper flex-just-center'>
                <div className='cracked'></div>
            </div>            
            }
        </div>
    )
}

export default Selection;

As you can see I'm attempting to call the setInit reducer from my redux store and then logging the new state after that. I also know that trying to log the state directly after doesn't always reflect the most recent data, but I've tried by adding a state change to my component and log the player state afterwards and I just get the same data back again. Nothing changes.

You try to get the player by doing export const selectPlayer = (state) => state.player; , but your state does not contain a player.

Also to use a selector with useSelector() you have to create your selector with the createSelector() method, like this: export const selectPlayer = createSelector((state) => state, (state) => state.player);

I think your initialState is your player , so if you want to access it you just have to return your state .

This might help you: https://redux-toolkit.js.org/api/createSelector

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM