Why does RTK query response handling not work?
Asked Answered
G

1

5

I tried to use the RTK query on my login request, but I got some trouble when printing out the result. Here is my code.

authRTK.ts

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { loginForm, UserResponse } from "../type/type";
import { RootState } from "./store";

export const api = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: 'http://localhost:3001',
    prepareHeaders: (headers, { getState }) => {
      // By default, if we have a token in the store, let's use that for authenticated requests
      const token = (getState() as RootState).auth.token;
      if (token) {
        headers.set("authentication", `Bearer ${token}`);
      }
      return headers;
    }
  }),
  endpoints: (build) => ({
    login: build.mutation<UserResponse, loginForm>({
      query: (credentials) => ({
        url: "login",
        method: "POST",
        body: credentials
      }),
      transformResponse: (response: { data: UserResponse }) => {
        return response.data
      },
    }),
    protected: build.mutation({
      query: () => "protected"
    })
  })
});

export const { useLoginMutation,useProtectedMutation } = api;

store.ts

import { configureStore } from '@reduxjs/toolkit'
import cartReducer from './cartRedux';
import userReducer from './authRedux';
import { api } from './authRTK';

export const store = configureStore({
    reducer:{
        cart: cartReducer,
        auth: userReducer,
        [api.reducerPath]: api.reducer,
    },
    middleware: (gDM) => gDM().concat(api.middleware),//getDefaultMiddleware
})

export type RootState = ReturnType<typeof store.getState>

export type AppDispatch = typeof store.dispatch

Login.tsx


const Login = () => {
  const [login, { isLoading,error,isError}] = useLoginMutation();
  const [showPassword,setShowPassword] = useState<boolean>(false);
  return (
    <Container>
      <Wrapper>
        {/* <button onClick={()=>testCookie()}>測試一下cookie</button> */}
        <Title>SIGN IN</Title>
        <Formik
          initialValues={{ email: "", password: "" }}
          validationSchema={Yup.object({
            password: Yup.string()
              .min(8, 'Must be 8 characters or higher')
              .required(),
            email: Yup.string().email('Invalid email address').required(),
          })}
          onSubmit = {  async (values, actions) => {
                try{
                  const result = await login(values);
                  if("data" in result){
                    console.log(result.data)
                  }else{
                    console.log((result.error as RequestError).data) ////this will printout the expected result , but I have to cast error to RequestError type to print the nested data inside , and I can't use this data else where like error above
                    console.log(error) //This printout undefined,mean there's no error data inside,but not supposed to happen
                    console.log(isError) //print out false , but supposed to be true
                  }
                }catch(err){
                  console.log(err)
                } 
                
          }}>
            {({
            errors,
            values,
            handleChange,
            handleBlur,
            handleSubmit,
            validateField
          }) => (
            <Form onSubmit={handleSubmit}>
                <InputContainer>
                <Input
                  onChange={handleChange}
                  onBlur={handleBlur}
                  value={values.email}
                  type="text"
                  name="email"
                  placeholder="Email"
                  data-testid="email"
                />
                </InputContainer>
                {errors.email && <Error data-testid="emailError">{errors.email}</Error>}

                <InputContainer>
                <Input
                  onChange={handleChange}
                  onBlur={handleBlur}
                  value={values.password}
                  type={showPassword ? "text" : "password"}
                  name="password"
                  placeholder="Password"
                  data-testid="password"
                />
                {showPassword ? <VisibilityOff onClick={()=>setShowPassword(false) }/> : <Visibility onClick={()=>setShowPassword(true) }/> }
                </InputContainer>
                {errors.password && <Error data-testid="passwordError">{errors.password}</Error>}
                
              <Button 
              data-testid="submit"
              type="submit">Submit</Button>
            </Form>
          )}
        </Formik>
      </Wrapper>
    </Container>
  );
};

export default Login;

So My main problems are with the login.tsx,Error didn't work as expected, and my response data have to determine if "data" is in it, even though I used transformResponse.

BTW my response type looks like below

RequestError:

{
    data:string;
    status:string
}

Glottochronology answered 6/11, 2021 at 1:44 Comment(0)
R
9

data is not the data from your response. It is the data property of the trigger function result.

trigger always returns an object in the form { data: ... } or { error: ... }.

So without your transformResult you would end up with result.data.data instead of result.data.

You can also unwrap that, to directly get the data and throw an error in the error case, but that's not the default as it might lead to uncaught promise rejection errors if you don't handle it.

async (values, actions) => {
                try{
                  const result = await login(values).unwrap();
                  console.log(result.data)
                } catch(err){
                  console.log(err)
                } 
                
          }
Reba answered 6/11, 2021 at 8:17 Comment(2)
sorry for response so late , but what do you mean by transformResult , since I used transformResponse , and I saw other example like mine , and it worked , also I tried above answer (unwrap) , then I got a promise instead of data.Glottochronology
Sorry I meant transformResponse. The general point is: you will always get an object with data back, completely independent of your code. That is how it signals you "there was no error". And as for the promise... my code above is awaiting that - you'll need to do that too.Reba

© 2022 - 2024 — McMap. All rights reserved.