Internal Error 'Already Read'
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,
],
},
}
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.
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
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)
}),
}
}
Create own getJSON :)
import { getJSON as noCloneGetJSON } from 'redux-api-middleware'
export function getJSON(res) {
return noCloneGetJSON(res.clone())
}