next-redux-wrapper icon indicating copy to clipboard operation
next-redux-wrapper copied to clipboard

Redux toolkit. reducers and extraReducer is work but useSelector (for type) dont give updated state.

Open Choco-milk-for-u opened this issue 2 years ago • 7 comments

Describe the bug

i have createAsyncThunk function. It makes request to server (axios) and then get data, after that extraReducers handle builder.addCase in it makes state.value = action.payload, then consol.log(state.value) writes value from server. Great! Its work but!! when i use useSelector it see existing value from initialState but get value only when it was first time initialized(null or just []) not updated after dispatch in wrapper.getServerSIdeProps . Same with just reducers and function in. it It works change state (console.log write it) but useSelector dont give me updated value

Code

Slice

import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import axios from 'axios';

import { HYDRATE } from 'next-redux-wrapper';
// types
import { IInitialStateV1 } from '../types/store';
import { ITrack } from '../types/tracks/track';

export const fetchData = createAsyncThunk('main/fetchData', async (): Promise<ITrack[]> => {
    const { data } = await axios.get<ITrack[]>('http://localhost:5000/track');

    return data;
})

const initialState: IInitialStateV1 = {
    pause: true,
    currentTime: 0,
    volume: 0,
    duration: 0,
    active: null,
    tracks: [],
}
export const mainSlice = createSlice({
    name: 'main',
    initialState,
    reducers: {
        setPause(state, action: PayloadAction<boolean>) {
            state.pause = action.payload;
        },
        setTime(state, action: PayloadAction<number>) {
            state.currentTime = action.payload;
        },
        setVolume(state, action: PayloadAction<number>) {
            state.volume = action.payload;
        },
        setDuration(state, action: PayloadAction<number>) {
            state.duration = action.payload;
        },
        setActive(state, action: PayloadAction<ITrack>) {
            state.active = action.payload;

            state.currentTime = 0;
            state.duration = 0;
        }
    },
    extraReducers: (builder) => {

        // [HYDRATE]: (state, action) => {
        //     return {
        //         ...state,
        //         ...action.payload,
        //     }
        // },
        //     [fetchData.fulfilled.toString()]: (state, action: PayloadAction<ITrack[]>) => {
        //         state.tracks = action.payload;
        //     }
        builder.addCase(HYDRATE, (state, action: any) => {
            return {
                ...state,
                ...action.payload,
            }
        }).addCase(fetchData.fulfilled, (state, action: PayloadAction<ITrack[]>) => {
            // return {
            //     ...state,
            //     ...action.payload,
            // }
            state.tracks = action.payload;
        });
    }
})

export const { setPause, setTime, setVolume, setDuration, setActive } = mainSlice.actions;
export default mainSlice.reducer;

Index

import { AnyAction, configureStore, ThunkDispatch } from '@reduxjs/toolkit';
import { createWrapper, MakeStore, Context } from 'next-redux-wrapper';

import mainRed from './index';

const makeStore = () => configureStore({
    reducer: {
        main: mainRed
    },
})

type AppStore = ReturnType<typeof makeStore>;

export type RootState = ReturnType<AppStore['getState']>;
export type AppDispatch = AppStore['dispatch'];
export type NextThunkDispatch = ThunkDispatch<RootState, void, AnyAction>;

export const wrapper = createWrapper<AppStore>(makeStore);

edited hooks

import { useDispatch, useSelector, TypedUseSelectorHook } from "react-redux";

import { RootState, AppDispatch } from "../store/reducer";

export const useTypeSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useTypeDispath = ()=> useDispatch<AppDispatch>();

getServerSIdeProps and useSelectore ( in page)

import { Container, ListItem, Stack, Box, Button } from "@mui/material";
import TrackList from "../../components/TrackList";
// interfaces
import { ITrack } from "../../types/tracks/track";
// import hooks
import { useRouter } from "next/router";
import { useTypeSelector } from "../../hooks/useTypeSelector";
// wrapper
import { NextThunkDispatch, wrapper } from "../../store/reducer";
import { fetchData, setVolume } from "../../store";

export default function Index(): JSX.Element {
  const router = useRouter();
  
  const tracks: ITrack[] = useTypeSelector(state => state.main.tracks);

  return (
    <div className="main">
      <Container >
        <Stack marginTop={20} sx={{ backgroundColor: "#C4C4C4", fontSize: '24px', fontWeight: 'bold' }}>
          <Box p={5} justifyContent="space-between">
            <ListItem>List of Tracks</ListItem>
            <Button variant="outlined" sx={{ backgroundColor: 'blue', color: 'white' }} onClick={() => router.push('/tracks/create')} >Upload</Button>
          </Box>
          <TrackList tracks={tracks} />
        </Stack>
      </Container>
    </div>
  )
}

export const getServerSideProps = wrapper.getServerSideProps((store) => async () => {
  const dispatch = store.dispatch as NextThunkDispatch;
  // dispatch(fetchData());
     dispatch(setVolume(2));

  return {
    props: {}
  }
})

Expected behavior

It supposed to dispatch function get value from server and be in useSelector.

Screenshots

If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

Windows, OperaGX.

Additional context

Add any other context about the problem here.

Choco-milk-for-u avatar May 13 '22 17:05 Choco-milk-for-u

I'm facing the same issue. Have you found something?

tiavina-mika avatar Jun 04 '22 07:06 tiavina-mika

I'm facing the same issue. Have you found something?

ContextApi instead of next-wrapper-redux

Choco-milk-for-u avatar Jun 04 '22 16:06 Choco-milk-for-u

I have the same issue useAppSelector not give any updated value

Rammehar avatar Jun 05 '22 10:06 Rammehar

I'm facing the same issue. Have you found something?

NeryC avatar Jul 27 '22 07:07 NeryC

Same issue here. Have you found any solution on this ? :)

naimlatifi5 avatar Aug 17 '22 17:08 naimlatifi5

I've managed to solve it by copying source code of the module (next-redux-wrapper) to my src folder and referencing from there. It works perfectly. I suspect that problem is somewhere at redux or react-redux. They are not resolved properly or using different versions.

roman-myroshnyk avatar Aug 25 '22 01:08 roman-myroshnyk

Do yarn why react-redux and yarn why redux to see which packages are installed and why, and let me know.

You can set exact version by using resolutions field in your package.json.

kirill-konshin avatar Aug 25 '22 03:08 kirill-konshin

Hi, action.payload has the state of the whole app, please console.log(action.payload) in HYDRATE builder, so this builder won't work

 builder.addCase(HYDRATE, (state, action: any) => {
            return {
                ...state,
                ...action.payload,
           }
}

try this way state = action.payload.main and persist the server state or state.currentTime = action.payload.main.currentTime

builder.addCase(HYDRATE, (state, action: any) => {
         state = action.payload.main
}

phumzani101 avatar Oct 11 '22 04:10 phumzani101

forget about tihs issue. Closed

Choco-milk-for-u avatar Dec 03 '22 22:12 Choco-milk-for-u