Last active
January 14, 2019 18:18
-
-
Save mattvague/cf0037d8ef48973dbd796e52d2cf320d to your computer and use it in GitHub Desktop.
How I do Normalizing / Denormalizing of JSONAPI data in redux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// This is quick demo of how I currenty normalize / denormalize JSON API responses into redux | |
// | |
import normalize from 'json-api-normalizer' | |
// Given this example response from a JSONAPI endpoint | |
const json = { | |
data: [{ | |
"type": "post-block", | |
"relationships": { | |
"question": { | |
"data": { | |
"type": "question", | |
"id": "295" | |
} | |
} | |
}, | |
"id": "2620", | |
"attributes": { | |
"text": "I am great!", | |
"id": 2620 | |
} | |
}], | |
included: [{ | |
"type": "question", | |
"id": "295", | |
"attributes": { | |
"text": "How are you?", | |
id: 295 | |
} | |
}] | |
} | |
/* | |
I want to transform that JSON into something that can stored in the redux store such | |
that there are is no nesting or duplication of resources, but also preserving those relationships | |
for later so that the data can be denormalized later for usage in components | |
*/ | |
/* | |
--------------- | |
Actions | |
--------------- | |
I'm definitely not sure this is the best way to do it, but I currently | |
normalize my json responses in my actions, which looks something like this | |
*/ | |
import normalize from 'json-api-normalizer' | |
// Async action which makes API request and then dispatches success action | |
function getRequest(path) { | |
return function (dispatch) { | |
fetch(path) | |
.then((response) => { | |
response.json().then((data) => { | |
dispatch(getRequestSuccess(data)) | |
}) | |
}) | |
} | |
} | |
// Success action which takes raw JSONAPI json and normalizes it with json-api-normalizer (https://github.com/yury-dymov/json-api-normalizer) // | |
function getRequestSuccess(json) { | |
return { | |
type: GET_REQUEST_SUCCESS, | |
payload: normalize(json) | |
} | |
} | |
/* | |
The dispatched action looks like this | |
{ | |
type: 'GET_REQUEST_SUCCESS', | |
payload: { | |
questions: { | |
{ | |
id: 295, | |
type: "question" | |
attributes: { | |
text: "How are you?" | |
} | |
} | |
}, | |
postBlock: { | |
"2620": { | |
id: 2620, | |
type: "postBlock", | |
attributes: { | |
text: "I am great!" | |
}, | |
relationships: { | |
question: { | |
type: "question", | |
id: "295" | |
} | |
} | |
} | |
} | |
} | |
*/ | |
/* | |
-------------- | |
Reducer | |
-------------- | |
This action is then caught by our reducer, merged (somehow) with the existing | |
json data, and chucked right into the store | |
*/ | |
function jsonApiReducer(action, state) { | |
switch (action.type) { | |
case GET_REQUEST_SUCCESS: | |
return return merge(action.payload) | |
} | |
} | |
/* | |
-------------- | |
Selector | |
-------------- | |
Now, I have a redux store populated with Map keyed by resource name each containing a map keyed by ID. | |
Let's say I want to list out all of the questions in the store. That will require re-joining all of the relationsghips | |
and turning the maps into an array and to do this I use redux-object (https://github.com/yury-dymov/redux-object) | |
*/ | |
import build from 'redux-object' | |
// I have a selector which calls redux-object | |
export const getQuestions = (state) => build(state, 'question') | |
// Which is then hooked up to whatever component in mapStateToProps | |
function mapStateToProps(state) { | |
return { | |
questions: getQuestions(state) | |
} | |
} | |
// Or I can also grab a single question with another selector | |
export const getQuestion = (state, id) => build(state, 'question', id) | |
function mapStateToProps(state, { id }) { | |
return { | |
question: getQuestions(state, id) | |
} | |
} | |
/* | |
Note: this is a pretty truncated version as I do a few other things like | |
- I use redux-resource to avoid a lot of boilerplate in dealing with REST APIs in redux | |
- I memoize my selectors with reselect to avoid expensive recomputation each render | |
*/ | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment