Search code examples
reactjsreduxreact-reduxredux-toolkitredux-thunk

have a problem to set user in my state , do it with createAsynceThunk from redux-toolkit


im dispatch the user from my component to the setUserAsync in my userSlice folder, but when im dispatch it, the user even not came to the createAsyncThunk in the console.log , my goal is to save the user in my state , and in the local storage.

im dispatch the user from my component to the setUserAsync in my userSlice folder, but when im dispatch it, the user even not came to the createAsyncThunk in the console.log , my goal is to save the user in my state , and in the local storage.

userSlice.js

import {createSlice , createAsyncThunk} from '@reduxjs/toolkit'
import {userService} from '../services/user-service'

const initialState = {
    user: null,
    loading:false
}


const userSlice = createSlice({
    name: 'user',
    initialState,
    reducers: {
        setUser(state, action) {
            state.user = action.payload
        },
    },
    extraReducers: (builder) => {
        builder.addCase(setUserAsync.pending, (state) => {
            state.user.status = 'loading'
        })
            .addCase(setUserAsync.fulfilled, (state, action) => {
                state.status = 'complete'
                state.user = action.payload
        })
            .addCase(setUserAsync.rejected, (state) => {
                state.status = 'failed'
               
        })
    }
})

export const setUserAsync = createAsyncThunk(
    'user/userservice',
    async (loggedInUser) => {
        console.log(loggedInUser);
        const user = await userService.login(loggedInUser)
        return user.data
    }
)

export const { setUser  } = userSlice.actions
export const selectUser = (state) => state.user.user
export default userSlice.reducer

userService.js

import { storageService } from "./storage-service";

export const userService = {
    login
}

const USER_KEY = 'user'

// let loggedInUser


export async function login(user) {
    console.log(user);
    const newUser = storageService.store(USER_KEY, user)
    console.log(newUser);
    return new Promise.resolve(newUser)
}

storageService.js

function store(key, value) {
    localStorage[key] = JSON.stringify(value);
}

function load(key, defaultValue = null) {
    var value = localStorage[key] || defaultValue;
    return JSON.parse(value);
}
export const storageService = {
    store,
    load
}

Solution

  • You should not use createAsyncThunk at all, because localStorage.setItem() is not async: Is HTML5 localStorage asynchronous?

    There is also no need to track this operation with a loading flag. You can simply assume that it happens immediately.

    What you want to do is:

    const initialState = {
        // Load previously persisted user or set it to null if none exists.
        user: JSON.parse(localStorage.getItem('__MY_REACT_APP_USER__'))
    }
    

    And then for the slice:

    const userSlice = createSlice({
        name: 'user',
        initialState,
        reducers: {
            setUser(state, action) {
                state.user = action.payload;
                localStorage.setItem('__MY_REACT_APP_USER__', JSON.stringify(action.payload));
            },
        }
    });
    

    Why __MY_REACT_APP_USER__ and not just user or USER or USER_KEY? You're risking a name collision by using a common word, other libraries might write to localStorage as well. It's better to obfuscate it a bit.