Created
May 2, 2016 16:53
-
-
Save markerikson/ca34306c54b21e7c9505b524cd537abe to your computer and use it in GitHub Desktop.
Redux-ORM tree data sample
This file contains 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
import { | |
schema, | |
getModelByType, | |
} from "models/models"; | |
import {UPDATE_ITEM_CHECK_STATES} from "constants/items"; | |
export const updateItemCheckState = (itemID, itemType, newCheckState) => (dispatch, getState) => { | |
const state = getState(); | |
const session = schema.from(state.entities); | |
const model = getModelByType(session, itemID, itemType); | |
let descendants = model.children; | |
const leaves = []; | |
const otherItems = []; | |
// Find all things underneath the clicked item, and split them into | |
// a list of A's (our "leaves") and a list of all other items | |
while(descendants.length > 0) { | |
const childItem = descendants.shift(); | |
const childType = childItem.getClass().modelName; | |
const itemsArray = (childType === "A") ? leaves : otherItems; | |
// Don't bother updating things that already have the right value | |
if(childItem.checked !== newCheckState) { | |
itemsArray.push({itemID : childItem.getId(), itemType : childType}); | |
} | |
descendants = descendants.concat(childItem.children); | |
} | |
dispatch({ | |
type : UPDATE_ITEM_CHECK_STATES, | |
payload : {itemID, itemType, newCheckState, leaves, otherItems}, | |
}); | |
// call server API with list of updated leaves | |
}; |
This file contains 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
import React, {Component} from "react"; | |
import {connect} from "react-redux"; | |
import {schema} from "models/models"; | |
import {isSelectedItemSelector} from "selectors/entities"; | |
const leafMapState = (state, ownProps) => { | |
const session = schema.from(state.entities); | |
const leafModel = session.A.withId(ownProps.itemID); | |
const leafData = leafModel.ref; | |
const leaf = { | |
...leafData, | |
checkState : leafModel.checkState.value, | |
}; | |
const selected = isSelectedItemSelector(state, ownProps); | |
return {leaf, selected}; | |
}; | |
class LeafComponent extends Component { | |
render() { | |
const {leaf, selected} = this.props; | |
// render appropriate stuff here | |
return <div /> | |
} | |
} | |
export default connect(leafMapState)(LeafComponent); |
This file contains 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
/* | |
My app has three main data types: A, B, and C. I have a treeview that displays items of each type | |
inside a Folder, with checkboxes for each type. A is considered a "leaf", can also appear under | |
B > D > A, and C > A. Also, A is the only type that has its checked state stored on the server. | |
For the others, I need to determine the checked state based on the combined values of their children | |
(all checked -> checked, none checked -> unchecked, some checked -> partial). This means I need to | |
be able to walk up and down the tree structure. | |
*/ | |
import {fk, many, oneToOne, Model, Schema} from "redux-orm"; | |
import * as folderConstants from "constants/folders"; | |
import _ from "lodash"; | |
export function getModelByType(session, itemID, itemType) { | |
const modelClass = session[itemType]; | |
const model = modelClass.withId(itemID); | |
return model; | |
} | |
class CheckState extends Model { | |
static generate(value = false) { | |
return this.create({value}); | |
} | |
} | |
CheckState.generate = CheckState.generate.bind(CheckState); | |
CheckState.modelName = "CheckState"; | |
class TreeItemWithChildren extends Model { | |
get children() { | |
return []; | |
} | |
get checked() { | |
return this.checkState.value; | |
} | |
set checked(newCheckedValue) { | |
this.checkState.value = newCheckedValue; | |
} | |
updateCheckState() { | |
// snipped: loop over children, determine check state based on theirs | |
} | |
} | |
class A extends TreeItemWithChildren { | |
static get fields() { | |
return { | |
checkState : oneToOne("CheckState") | |
}; | |
} | |
get parent() { | |
switch(this.type) { | |
case "TYPE_1" : | |
return Folder.withId(folderConstants.FOLDER_TYPE_1); | |
case "TYPE_2" : | |
return this.b; | |
case "TYPE_3" : | |
return this.d.first(); | |
case "TYPE_4" : | |
return this.c.first(); | |
default : | |
break; | |
} | |
} | |
} |
This file contains 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
import {UPDATE_ITEM_CHECK_STATES} from "constants/items"; | |
import _ from "lodash"; | |
import {schema, getModelByType} from "models/models"; | |
export function updateCheckStateForModels(state, action) { | |
const {payload} = action; | |
const session = schema.from(state); | |
const {newCheckState, leaves, otherItems, itemID, itemType} = payload; | |
// Create a list of all changed items, clicked item first | |
const allChangedItems = [{itemID, itemType}].concat(leaves, otherItems); | |
// Look up all explicitly changed items, hydrate models, and update their checkstates | |
const allChangedModels = allChangedItems.map(item => { | |
const itemModel = getModelByType(session, item.itemID, item.itemType); | |
itemModel.checked = newCheckState; | |
return itemModel; | |
}); | |
const startingModel = _.first(allChangedModels); | |
let ancestor = startingModel.parent; | |
// Recurse upwards and recalculate dependent checkstates from the new values | |
while(ancestor) { | |
ancestor.updateCheckState(); | |
ancestor = ancestor.parent; | |
} | |
return session.reduce(); | |
} | |
export default function entitiesReducer(state = initialState, action) { | |
switch(action.type) { | |
case UPDATE_ITEM_CHECK_STATES: | |
return updateCheckStateForModels(state, action); | |
default: | |
return state; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment