redux-api-middleware icon indicating copy to clipboard operation
redux-api-middleware copied to clipboard

Internal Error 'Already Read'

Open mpolichette opened this issue 9 years ago • 4 comments

Hey guys, I'm really excited to use this library.

I started implementing some logic, but I am trying to use type descriptors to process the response from my api, however when I define a function for both the payload and the meta If i try to access the body from both, I get an error on the second attempt to read the JSON response.

Am I not processing it correctly?

Here is an example:

{
  [CALL_API]: {
    method: 'POST',
    endpoint: (state) => endpoint(state, '/create'),
    headers: (state) => headers(state, {}, { auth: true }),
    body: JSON.stringify(data),
    types: [
      types.REQUEST,
      {
        type: types.RECEIVE_CREATE_EVENT,
        payload: (action, state, res) => 
          getJSON(res).then((json) => normalize(json, schema)),
        meta: (action, state, res) => 
          getJSON(res).then((json) => {
            // !!! This never gets called because getJSON(res) causes an 'Already Read' error
            const { id } = json
            return { notify: `Created Id: ${id}` }
          }),
      },
      types.REQUEST_CREATE_EVENT_FAILED,
    ],
  },
}

mpolichette avatar Jul 14 '16 18:07 mpolichette

Current work around for me is to mutate the res object in the payload since it will be called first first.

I just do this:

getJSON(res).then((json) => {
  res.json = json // eslint-disable-line
  return normalize(json, schema)
}),

Edit: I've cleaned up my hack into a reusable function which isolates it.

const key = Symbol('JSON')
export function getAttachedJSON(response) {
  // This is a whole hack for getting json once and returning it the same way
  // once or multiple times.  It works by mutating the response
  if (response.key) {
    return Promise.resolve(response.key)
  }
  return getJSON(response).then((json) => {
    response[key] = json // eslint-disable-line no-param-reassign
    return json
  })
}

I could see this being implemented into the current getJSON function... though its really not the cleanest approach. I could also see some support for a way to preprocess the response before both the payload and meta are called.

mpolichette avatar Jul 14 '16 18:07 mpolichette

Seems like the other way to solve this would be for you to pass a clone of the response object: res.clone() https://developer.mozilla.org/en-US/docs/Web/API/Request/clone

Which could also be done in our own functions... but is probably better in the library

mpolichette avatar Jul 14 '16 19:07 mpolichette

I just fixed problem similar to yours. I found out that you cannot acces payload in meta property. There is just no way. You need to divide your logic between meta and payload property.

   {
    type: types.RECEIVE_CREATE_EVENT,
    payload: (action, state, res) => {
      getJSON(res).then((json) => {
        const { id } = json
        handleTheseData({ notify: `Created Id: ${id}` })
        return normalize(json, schema)
      }),
   }  
  }

developer239 avatar Feb 16 '17 14:02 developer239

Create own getJSON :)

import { getJSON as noCloneGetJSON } from 'redux-api-middleware'

export function getJSON(res) {
  return noCloneGetJSON(res.clone())
}

unrevised6419 avatar Sep 17 '19 08:09 unrevised6419