Skip to content Skip to sidebar Skip to footer

How To Use This.props.action Function In Typescript?

My authActions.tsx file looks like this: import { LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE } from './types'; import axios from 'axios'; import { Dispatch } from 're

Solution 1:

If the function returns void, it means you're returning nothing, you can't get the property then. You need to return a Promise

interface IProps {
    auth: {
        payload?: {
            success? : boolean,
            status?: number,
            message?: string,
            user?: {
                id: string,
                email: string
            },
            access_token?: string,
            expires_in?: string
        }
    };
    login: () =>Promise<void>;
}

Solution 2:

Since you're calling this.props.login(...).then(...), I assume login should return a Promise. You just need to type it as such in your IProps interface. Additionally, it looks like it's expected to take an argument containing email and password, so you should type that as well.

interface IProps {
  login: (credentials: { email: string; password: string; }) =>Promise<void>;
}

Solution 3:

Your setup is illogical because you are using the type IProps as the type for both:

  • The props of the Login component
  • The root state of your Redux store passed as an argument to mapStateToProps

You are expecting your component to take a prop userLogin which is a function. This function is not a property of your Redux store state and therefore it cannot possibly be true that the Redux state and the component props share the same type.

If you fix the IProps to accurately represent the component props then you will have problems with mapStateToProps and the react-redux connect HOC because they are using (state: IProps). You have already discovered that in your replies to @Kakiz's answer.


You should not need to determine your redux state type in this component's file because that type is global. The react-redux docs recommend that you infer the RootState type from your store variable, like this:

exporttypeRootState = ReturnType<typeof store.getState>

You put that line in the file where you create the store import the RootState type wherever you need it, like in mapStateToProps.

Their excellent guide on usage with TypeScript includes a whole section on Typing the connect higher order component, though they recommend using function components and hooks instead as these are much easier to type properly.


Don't be afraid to export types and reuse them. The ILoginObject defined in your actions file is identical to the IState defined in your component. Your IProps and ILoginDispatch have major duplications as well.

I often find it easier to write simple event handlers inline because then you avoid needing to type the e argument.


You need the TypeScript types to know that you have redux-thunk middleware installed in order to for it to know that dispatching an action will return a Promise, since this is not the default behavior.

The userLogin thunk itself can be simplified with the createAsyncThunk function from the official Redux Toolkit. This will also handle errors in the axios.post request itself, which you aren't currently catching. It's also a good candidate for RTK Query, which just got integrated into Redux Toolkit.


Here's a better setup for your component, which is completely type-safe although there are no types anywhere. Everything is inferred.

importReact, { useState } from"react";
import { unwrapResult } from"@reduxjs/toolkit";
import { userLogin } from"../store/slice";
import { useSelector, useDispatch } from"../store";

// no props needed anymoreexportdefaultfunctionLogin() {
  // not sure where you actually use thisconst auth = useSelector((state) => state.auth);

  const dispatch = useDispatch();

  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  constonSubmit = () => {
    dispatch(userLogin({ email, password }))
      .then(unwrapResult) // explained here: https://redux-toolkit.js.org/api/createAsyncThunk#unwrapping-result-actions
      .then((data) =>alert(`logged in user ${data.user}`))
      .catch((error) =>alert(`login error: ${error.message}`));
  };

  return (
    <React.Fragment><div><label>
          Email
          <inputtype="text"name="email"value={email}onChange={(e) => setEmail(e.target.value)}
          />
        </label></div><div><label>
          Password
          <inputtype="password"name="password"value={password}onChange={(e) => setPassword(e.target.value)}
          />
        </label></div><div><buttononClick={onSubmit}>Submit</button></div></React.Fragment>
  );
}

That is based on the following setup of store:

import { AnyAction, configureStore } from"@reduxjs/toolkit";
import {
  TypedUseSelectorHook,
  useDispatch as _useDispatch,
  useSelector as _useSelector
} from"react-redux";
import { logger } from"./middleware";
import auth from"./slice";

const store = configureStore({
  reducer: {
    auth
  }
});

exporttypeRootState = ReturnType<typeof store.getState>;
exporttypeAppDispatch = typeof store.dispatch;

exporttypeAction = AnyAction;

exportconstuseDispatch: () =>AppDispatch = _useDispatch;

exportconstuseSelector: TypedUseSelectorHook<RootState> = _useSelector;
exportdefault store;

And reducer/actions:

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

exportinterfaceAuthData {
  success?: boolean;
  status?: number;
  message?: string;
  user?: {
    id: string;
    email: string;
  };
  access_token?: string;
  expires_in?: string;
}

exportinterface ILoginObject {
  email: string;
  password: string;
}

exportconst userLogin = createAsyncThunk(
  "auth/Login",
  async (loginObject: ILoginObject, { rejectWithValue }) => {
    const response = await axios.post<AuthData>(`/api/login`, loginObject);

    if (response.data && response.data.access_token) {
      // returned value is the success payloadreturn response.data;
    } else {
      // alternatively, can throw an ErrorreturnrejectWithValue(response.data);
    }
  }
);

constinitialState: AuthData = { success: false };

const authSlice = createSlice({
  initialState,
  name: "auth",
  // reducers creates both actions and reducer casesreducers: {
    logout: (state, action) => {
      // clears the state and replaces with the initial statereturn initialState;
    }
  },
  // extraReducers adds reducer cases for existing actionsextraReducers: (builder) =>
    builder
      .addCase(userLogin.fulfilled, (state, action) => {
        // TODO - this replaces the entire state, probably not what you actually wantreturn action.payload;
      })
      .addCase(userLogin.rejected, (state, action) => {
        // TODO
      })
      .addCase(userLogin.pending, (state, action) => {
        // TODO - what is the correct number?
        state.status = 0;
      })
});

exportconst { logout } = authSlice.actions;
exportdefault authSlice.reducer;

You'll want to fill in some blanks in the reducer.

Code Sandbox Link

Post a Comment for "How To Use This.props.action Function In Typescript?"