Search code examples
javascriptreactjsoopreduxredux-saga

How to call API from redux-saga by effect call when the function to call API is defined in class


I'm trying to call an API by effect call from redux-saga but when will start to call to api throw me an error. Cannot read property 'post' of null

Is really weird because I created a simple function to test my call working example:

function requestGetUser() {
  return axios.request({
    method: 'get',
    url: 'https://my-json-server.typicode.com/atothey/demo/user',
  })
}

export function* fetchLoginSaga(action) {
  const { data } = yield call(requestGetUser)
  yield put(login({ id: data.id, name: data.firstName }))
}

export function* watcherSaga() {
  yield takeLatest(fetchLogin.type, fetchLoginSaga)
}

and with this example above works

but when I tried to call a API from class inside of effect call doesn't works

// Depedencies
import axios from 'axios'

export default class BaseService {
  /**
   * Default http request instane
   * @type { Axios.Instance }
   */
  instance

  /**
   * Creates an instance of BaseService.
   * @param { String } endpoint To manipulates the operations.
   * @param { String } [baseUrl= ] The base url.
   */
  constructor(endpoint, baseURL = process.env.BASE_URL) {
    this.endpoint = endpoint
    this.instance = axios.create({ baseURL })
    this.instance.interceptors.response.use(this.responseInterceptor, this.handleResponseError)
  }

  /**
   * Intercepts every response.
   * @param { Object } response The response.
   * @returns { Promise<Object> }
   */
  responseInterceptor = response => response.data

  /**
   * Intercepts every error on response.
   * @param { Object } error The respone error.
   * @returns { Promise<Object> }
   */
  handleResponseError = error => {
    const {
      response: {
        data: { message },
      },
    } = error
    return Promise.reject(new Error(message))
  }

  /**
   * Make a post request with data.
   * @param { Object } [data={}] The data to send as body.
   * @param { Object } [requestParams={}] The params to make the request.
   * @return { Promise<any> }
   */
  post = (data, { url = this.endpoint, ...rest } = {}) => {
    
    const response = this.instance.post(url, data, { ...rest })

    return response
  }

}

import BaseService from './baseService'

export class AuthService extends BaseService {
  /**
   * Initializes Auth Service.
   */
  constructor() {
    super('/auth/local')
  }

  /**
   * Logs Current user.
   * @param { String } identifier - User's Identifier
   * @param { String } password - User´s password.
   * @return { Promise<String> } jwt access token.
   */
  async login(identifier, password) {
    const user = await this.post({ password, identifier }, { url: '/auth/local' })// when I call return cant find post of null
    return user
  }
}

export default AuthService
import axios from 'axios'
import { call, takeLatest } from 'redux-saga/effects'

import { fetchLogin } from './authReducer'

import AuthService from 'api/authService'

const authService = new AuthService()

export function* fetchLoginSaga(action) {
  const response = yield call(authService.login, 'email', 'password')
  console.log({ response })// don't print
  
}

export function* watcherSaga() {
  yield takeLatest(fetchLogin.type, fetchLoginSaga)
}

enter image description here


Solution

  • behind the scenes, redux-saga will call your function with .apply, missing the this context of the authService object,

    detailed information in the repository issue: https://github.com/redux-saga/redux-saga/issues/27

    you have 2 ways to fix that:

    1. Change the call signature to yield call([auth, 'login'], "email", "password");
    2. You can use the apply effect instead - yield apply(auth, auth.login, ["email", "password"]);

    or you can bind the parent object to the auth.login function with plain JavaScript:

    • yield call(authService.login.bind(authService), 'email', 'password')

    I would recommend using the correct effect or effect signature for that instead! 😁

    the documentation of "context"/"fn" can be found in redux-saga docs: https://redux-saga.js.org/docs/api/#callcontext-fn-args