Skip to content

Instantly share code, notes, and snippets.

@mattvague
Last active January 14, 2019 18:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattvague/cf0037d8ef48973dbd796e52d2cf320d to your computer and use it in GitHub Desktop.
Save mattvague/cf0037d8ef48973dbd796e52d2cf320d to your computer and use it in GitHub Desktop.
How I do Normalizing / Denormalizing of JSONAPI data in redux
//
// 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