Kai021195
Kai021195

Reputation: 833

Why does RTK query response handling not work?

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
}

Upvotes: 5

Views: 20242

Answers (1)

phry
phry

Reputation: 44336

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)
                } 
                
          }

Upvotes: 9

Related Questions