Typescript - cannot find accessToken - javascript

I am writing a redux-toolkit create function and it is the first time I am using typescript. I fixed the error from yesterday and get directly a new one.
Currently Vs-code underlines accessToken and tells me:
The property accessToken does not exist for the User[] type.ts(2339)
I googled this and have found, that I should add the property to the interface but this brought no solution. And I ask myself why typescript says, this would be incorrect, when it works fine without typescript.
Here the current state:
type AsyncThunkConfig = {
state: RootState
}
export const createCardImage = createAsyncThunk<object, object, AsyncThunkConfig>('cardImages/create', async (cardImagesData, thunkAPI)=>{
try{
const token = thunkAPI.getState().auth.user!.accessToken;
return await cardImagesService.createCardImages(cardImagesData, token);
}catch (error:any) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message as string)
}
})
My auth initialstate and slice:
const user = JSON.parse(localStorage.getItem('user') || '');
interface User {
vorname:string;
nachname:string;
username:string;
email:string;
street:string;
number:string;
plz:number;
city:string;
password:string;
isAdmin:boolean;
createdAt: Date;
accessToken?:string;
}
interface InitialState{
user:User[] | null;
isLoading:boolean;
isSuccess:boolean;
isError:boolean;
message: string;
auth?:boolean;
}
const initialState: InitialState = {
user: user ? user : null,
isLoading:false,
isSuccess:false,
isError:false,
message:"",
};
slice without extraReducers:
const authSlice = createSlice({
name: 'auth',
initialState,
reducers:{
reset: (state)=>{
state.isLoading = false;
state.isSuccess = false;
state.isError = false;
state.message = "";
},
},

AsyncThunkConfig is the third type parameter. You also need to provide types for Returned and ThunkArg.
Try something like:
const createCardImage = createAsyncThunk<
// Returned
void, // whatever your return type is
// ThunkArg
CardImagesData, // the type of cardImagesData
AsyncThunkConfig
>

Related

createAsyncThunk abort() behaving abnormally

I am trying to optimize my react code by fixing any memory leaks. For this i am using createAsyncThunk canceling-while-running to cancel requests in case my component unmounts. Below is my useEffect from my component
useEffect(() => {
const data = { page: curPage }
const promise = dispatch(blockedUsers(data))
return () => promise.abort()
}, [curPage])
Below is my async thunk reducer
export const blockedUsers = createAsyncThunk(
"admin/blocked",
async (data, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await axios.get(API_URL + "/blocked");
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
below is my buider for my async thunk reducer
.addCase(blockedUsers.pending, (state) => {
state.isLoading = true;
})
.addCase(blockedUsers.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.blocked.users = action.payload.result;
state.blocked.users.length === 0
? (state.blocked.total = 0)
: (state.blocked.total = action.payload.count.count);
})
.addCase(blockedUsers.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
console.log(action);
toast.error(
"Could not get blocked users. Reason: " + state.message,
unsuccessful
);
When my coponent loads two requests are made to the server due to react strictmode. The first request gets rejected everytime no matter what and the second one gets fullfiled.
my dev tools screenshot.
Because of this i get a toast error for rejected request but at the same time i get the required data rendered in my application.
And i get undefined error which is given below:
type: 'admin/blocked/rejected', payload: undefined, meta: {…}, error: {…}}
error
:
{name: 'AbortError', message: 'Aborted'}
meta
:
{arg: {…}, requestId: '9H3Z2GZ1_A1hBxH4Di', rejectedWithValue: false, requestStatus: 'rejected', aborted: true, …}
payload
:
undefined
type
:
"admin/blocked/rejected"
[[Prototype]]
:
Object
I am unable to fix this. And i am not sure how will this behave once deployed. Please help. Thanks in advance.

How can I ignore empty parameters in express application

I have an api that requires some parameters to filter based on the the passed in parameter. It is similar top localhost:8000/api/v1/products?page=1&user=62831ebc8cecf3c829c4b2d9&category=62831ebc8cecf3c829c4b2d9
So I created a react action
export const listProducts =
(page = '', user = '', category = '') =>
async (dispatch) => {
try {
dispatch({ type: PRODUCT_LIST_REQUEST })
const { data } = await axios.get(
`${process.env.REACT_APP_API}/api/v1/products?&page=${page}&user=${user}&category=${category}`
)
dispatch({
type: PRODUCT_LIST_SUCCESS,
payload: data,
})
} catch (error) {
dispatch({
type: PRODUCT_LIST_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
The Idea is that when I when I dispatch dispatch(listProducts(1, user._id) and I leave the 3rd parameter empty, It returns Invalid category ''
How can I make the api ignore unsent paramaters and only use sent parameters.
I have tried replacing (page = '', user = '', category = '') with (page = null, user = null, category = null)
Now i get Invalid category null
EDIT: In some instances, i would want to pass only categories while in some other instances, I would want to pass user and page only. How can I do this ${process.env.REACT_APP_API}/api/v1/products?&page=${page}&user=${user}&category=${category} to only accept data in the object and ignore what's not available
If the number of parameters passed to the function is frequently changing, or if you don't have control on what will be passed to the function I would achieve this by using a single object as a parameter rather than multiple parameters.
Example:
Instead of export const listProducts = (page = '', user = '', category = '') => you can pass a single object and deconstruct the fields from the object like:
export const listProducts = ({page, user, category }) =>
//will be passed like:
listProducts({
page: 1,
user: data.user,
category: ''
})
Why?
You don't have to control the order of the parameters while passing arguments to the function and if a field is not passed to the function; it will evaluate to undefined:
const obj1 = {
page: 1,
user: 'test',
category: 'first'
}
const obj2 = {
page: 2,
user: 'test2'
}
const obj3 = {
user: 'test3'
}
const listProducts = ({page, user, category}) => {
const str = `Parameters:\n page: ${page}\nuser: ${user}\ncategory: ${category}`
console.log(str)
}
listProducts(obj1)
listProducts(obj2)
listProducts(obj3)
console.log("\n-------------DEFAULT VALUES------\n")
/* Default value example: */
const listProducts2 = ({page = 1, user = '', category = ''}) => {
const str = `Parameters:\n page: ${page}\nuser: ${user}\ncategory: ${category}`
console.log(str)
}
listProducts2(obj1)
listProducts2(obj2)
listProducts2(obj3)
Ignoring undefined parameters (may not be efficient, just a try):
let url = `${process.env.REACT_APP_API}/api/v1/products?`
if(user) url += `&user=${user}`
if(page) url += `&page=${page}`
if(category) url += `&category=${category}`

use async function to get draft inside reducer of useImmerReducer

I have this reducer function that I use for state management of my app.
const initialState = {roles: null};
const reducer = (draft, action) => {
switch (action.type) {
case 'initialize':
//what should i do here????
return;
case 'add':
draft.roles = {...draft.roles, action.role};
return;
case 'remove':
draft.roles = Object.filter(draft.roles, role => role.name != action.role.name);
}
};
const [state, dispatch] = useImmerReducer(reducer, initialState);
to initialize my state I must use an async function that reads something from asyncStorage if it exists, must set draft.roles to it, if not it should be set to a default value.
const initialize = async () => {
try {
let temp = await cache.get();
if (temp == null) {
return defaultRoles;
} else {
return temp;
}
} catch (error) {
console.log('initialization Error: ', error);
return defaultRoles;
}
};
how can I get initilize function returned value inside 'initialize' case? if I use initilize().then(value=>draft.roles=value) I get this error:
TypeError: Proxy has already been revoked. No more operations are allowed to be performed on it
You cannot use asynchronous code inside of a reducer. You need to move that logic outside of the reducer itself. I am using a useEffect hook to trigger the initialize and then dispatching the results to the state.
There are quite a few syntax errors here -- should state.roles be an array or an object?
Here's my attempt to demonstrate how you can do this. Probably you want this as a Context Provider component rather than a hook but the logic is the same.
Javascript:
import { useEffect } from "react";
import { useImmerReducer } from "use-immer";
export const usePersistedReducer = () => {
const initialState = { roles: [], didInitialize: false };
const reducer = (draft, action) => {
switch (action.type) {
case "initialize":
// store all roles & flag as initialized
draft.roles = action.roles;
draft.didInitialize = true;
return;
case "add":
// add one role to the array
draft.roles.push(action.role);
return;
case "remove":
// remove role from the array based on name
draft.roles = draft.roles.filter(
(role) => role.name !== action.role.name
);
return;
}
};
const [state, dispatch] = useImmerReducer(reducer, initialState);
useEffect(() => {
const defaultRoles = []; // ?? where does this come from?
// always returns an array of roles
const retrieveRoles = async () => {
try {
// does this need to be deserialized?
let temp = await cache.get();
// do you want to throw an error if null?
return temp === null ? defaultRoles : temp;
} catch (error) {
console.log("initialization Error: ", error);
return defaultRoles;
}
};
// define the function
const initialize = async() => {
// wait for the roles
const roles = await retrieveRoles();
// then dispatch
dispatch({type: 'initialize', roles});
}
// execute the function
initialize();
}, [dispatch]); // run once on mount - dispatch should not change
// should use another useEffect to push changes
useEffect(() => {
cache.set(state.roles);
}, [state.roles]); // run whenever roles changes
// maybe this should be a context provider instead of a hook
// but this is just an example
return [state, dispatch];
};
Typescript:
import { Draft } from "immer";
import { useEffect } from "react";
import { useImmerReducer } from "use-immer";
interface Role {
name: string;
}
interface State {
roles: Role[];
didInitialize: boolean;
}
type Action =
| {
type: "initialize";
roles: Role[];
}
| {
type: "add" | "remove";
role: Role;
};
// placeholder for the actual
declare const cache: { get(): Role[] | null; set(v: Role[]): void };
export const usePersistedReducer = () => {
const initialState: State = { roles: [], didInitialize: false };
const reducer = (draft: Draft<State>, action: Action) => {
switch (action.type) {
case "initialize":
// store all roles & flag as initialized
draft.roles = action.roles;
draft.didInitialize = true;
return;
case "add":
// add one role to the array
draft.roles.push(action.role);
return;
case "remove":
// remove role from the array based on name
draft.roles = draft.roles.filter(
(role) => role.name !== action.role.name
);
return;
}
};
const [state, dispatch] = useImmerReducer(reducer, initialState);
useEffect(() => {
const defaultRoles: Role[] = []; // ?? where does this come from?
// always returns an array of roles
const retrieveRoles = async () => {
try {
// does this need to be deserialized?
let temp = await cache.get();
// do you want to throw an error if null?
return temp === null ? defaultRoles : temp;
} catch (error) {
console.log("initialization Error: ", error);
return defaultRoles;
}
};
// define the function
const initialize = async() => {
// wait for the roles
const roles = await retrieveRoles();
// then dispatch
dispatch({type: 'initialize', roles});
}
// execute the function
initialize();
}, [dispatch]); // run once on mount - dispatch should not change
// should use another useEffect to push changes
useEffect(() => {
cache.set(state.roles);
}, [state.roles]); // run whenever roles changes
// maybe this should be a context provider instead of a hook
// but this is just an example
return [state, dispatch];
};

How to return a promise from redux thunk action and consume it in component

I am using React+Redux+Redux Thunk + Firebase authentication. Writing code in Typescript.
My action is:
//Type for redux-thunk. return type for rdux-thunk action creators
type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
IStoreState, //my store state
null,
Action<userActionTypes>
>
export const signInWithEmailAndPasword =(email:string, pasword:string): AppThunk=>{
return async (dispatch)=>{
auth.signInWithEmailAndPassword(email, pasword).then(response=>{
if(response.user){
const docRef = db.collection("users").doc(response.user.uid);
docRef.get().then(function(doc) {
if (doc.exists) {
const userData = doc.data(); //user data from firebase DB
//if user exists in DB, dispatch
dispatch({
type: userActionTypes.SIGN_IN_USER,
payload: userData
})
return userData;
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
}
})
.catch(err=> dispatch(setUserError(err.message)))
}
}
My SignIn component, where i am dispatching this above action:
import React, { useState } from 'react'
//some other imports
//...
//
import { useDispatch, useSelector } from 'react-redux';
import { signInWithEmailAndPasword } from '../../redux/actions/userActions';
interface ISignInState {
email: string;
password: string;
}
const SignIn = (props:any) => {
const [values, setValues] = useState<ISignInState>({ email: '', password: '' })
const dispatch = useDispatch();
const handleInputChange = (e: React.FormEvent<HTMLInputElement>): void => {
const { name, value } = e.currentTarget;
setValues({ ...values, [name]: value })
}
const handleFormSubmit = (e: React.FormEvent) => {
e.preventDefault()
const { email, password } = values;
dispatch(signInWithEmailAndPasword(email, password))
//// -> gives error: Property 'then' does not exist on
//// type 'ThunkAction<void, IStoreState, null, Action<userActionTypes>>'
.then(()=>{
props.history.push('/');
setValues({ email: '', password: '' })
})
}
return (<div>Sign in UI JSX stuff</div>)
So when i try to use .then() after dispatch(signInWithEmailAndPasword(email, password)) it gives an error Property 'then' does not exist on type 'ThunkAction<void, IStoreState, null, Action<userActionTypes>>'
So how can i return promise from redux action and chain a .then() on it? I always assumed that thunk actions return promises by default.
Thanks for your help
Edit:
Temporary soluton was to use any as return type of above action:
export const signInWithEmailAndPasword = (email:string, pasword:string):any =>{
return async (dispatch: any)=>{
try {
const response = await auth.signInWithEmailAndPassword(email, pasword)
if(response.user){
const userInDb = await getUserFromDB(response.user)
dispatch(userSignIn(userInDb))
return userInDb
}
} catch (error) {
dispatch(setUserError(error.message))
}
}
}
But I don't want to use any
Just add return before this line:
auth.signInWithEmailAndPassword(email, pasword).then(response=>{
So it would be:
export const signInWithEmailAndPasword =(email:string, pasword:string): AppThunk=>{
return async (dispatch)=>{
return auth.signInWithEmailAndPassword(email, pasword).then(response=>{
It should work.
AppThunk<Promise<void>>
You need to explicitly declare the AppThunks return type, which in this case should be a Promise containing nothing. You have already made it async so just make sure to enter the correct AppThunk return type
export const signInWithEmailAndPassword = (email: string, password: string): AppThunk<Promise<void>> => {
return async (dispatch) => {
// do stuff
}
}
Thunks return functions, not promises. For this you could look at redux-promise. But to be honest if your doing something this complex you would be much better off using redux-saga.
Another approach would be to use the concepts behind redux-api-middleware to create your own custom redux middleware. I have done this in the past to connect a message queue to redux.

variable is assigned a value but never used when declaring it in Vue data object and using it in a method

I'm getting an error in the webpack cmd window when I run npm run dev on a project.
Here is the code and the error message that I get specifically, the code related to the parent top-level Vue component, which has the navbar in that has details that change depending on if the user is logged in:
The code
<script>
// import required components
import EventBus from './components/EventBus'
import router from './router/index.js'
import jwtDecode from 'jwt-decode'
export default {
data () {
const token = localStorage.usertoken
const decoded = jwtDecode(token)
return {
first_name: '',
surname: '',
email: '',
created: ''
}
return {
auth: false
}
try {
this.login()
} catch (error) {
console.log('Not currently signed in')
}
},
methods: {
logout () {
this.first_name = ''
this.surname = ''
this.email = ''
this.created = ''
localStorage.removeItem('usertoken')
this.auth = false
router.push({
name: 'login'
})
},
login () {
this.first_name = this.decoded.f_name
this.surname = this.decoded.s_name
this.email = this.decoded.email
this.created = this.decoded.created
}
},
mounted () {
EventBus.$on('logged-in', status => {
this.auth = status
this.login()
})
}
}
</script>
And the error message
✘ http://eslint.org/docs/rules/no-unused-vars 'decoded' is assigned a value but never used
src\App.vue:60:11
const decoded = null
To me, it looks like decoded is used in login(), any ideas?
you need to change in your data method
As your data is a function and what gets exposed is return value. you need to return decoded from data()in order to use decoded in your login method.
data () {
const token = localStorage.usertoken
const decoded = jwtDecode(token)
return {
first_name: '',
surname: '',
email: '',
created: '',
decoded: decoded
}

Categories