Skip to content

Instantly share code, notes, and snippets.

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 jribeiro/f82d2e16dfcaba7fa8d4f29f975e71dd to your computer and use it in GitHub Desktop.
Save jribeiro/f82d2e16dfcaba7fa8d4f29f975e71dd to your computer and use it in GitHub Desktop.
Redux form editing HOC
import React, {Component} from 'react';
import {bindActionCreators} from "redux"
import { connect } from 'react-redux';
import {schema, getModelByType} from "models/models"
import SomeFormComponent from "SomeFormComponent"
import {selectedItemSelector, isEditingSelector} from "selectors/entities"
let mapStateToProps = (state) => {
let selectedItem = selectedItemSelector(state);
let isEditing = isEditingSelector(state);
let name = "Unknown";
let type = "N/A";
let item;
if(selectedItem) {
let {itemID, itemType} = selectedItem;
let entities = isEditing ? state.selection.editingEntities : state.entities;
let session = schema.from(entities);
let model = getModelByType(session, itemID, itemType);
name = model.name;
type = itemType;
item = model.toJSON();
}
return {name, type, item, isEditing};
}
const mapDispatchToProps = (dispatch) => ({
actions : bindActionCreators({editItem, stopEditing}, dispatch)
})
// Do useful stuff with either the current selected item,
// or the "work-in-progress editing" version of the item
class FormContentsContainer extends Component {
render() {
let {type, item, isEditing} = this.props;
return (
<FormEditWrapper value={item} isEditing={isEditing}>
<SomeFormComponent />
</FormEditWrapper>
);
}
import React, {Component, PropTypes} from "react";
import {noop, debounce, defaults, values} from "lodash";
export function getValueFromEvent(e) {
const {target} = e;
let newValues;
if(target) {
const value = (target.type === "checkbox") ? target.checked : target.value;
newValues = {
[target.name] : value,
};
}
else if(isObject(e)) {
newValues = e;
}
return newValues;
}
class FormEditWrapper extends Component {
static propTypes = {
value : PropTypes.object.isRequired,
isEditing : PropTypes.bool,
onChange : PropTypes.func,
valuePropName : PropTypes.string,
onChangePropName : PropTypes.string,
singleValue : PropTypes.bool,
passIsEditing : PropTypes.bool,
}
static defaultProps = {
isEditing : true,
onChange : noop,
valuePropName : "value",
onChangePropName : "onChange",
singleValue : false,
passIsEditing : true,
}
constructor(props) {
super(props);
const boundDispatchAttributes = this.dispatchAttributeChange.bind(this);
this.dispatchAttributeChange = debounce(boundDispatchAttributes, 250);
this.state = {
value : {},
};
}
componentWillReceiveProps() {
// Reset any component-local changes Note that the incoming props
// SHOULD match the changes we just had in local state.
this.setState({value : {}});
}
onChildChange = (e) => {
const {isEditing} = this.props;
if(isEditing) {
const newValues = getValueFromEvent(e);
if(newValues) {
const change = {
...this.state.value,
...newValues
};
// Update our component-local state with these changes, so that the child components
// will re-render with the new values right away
this.setState({value : change});
// Because this is debounced, we will only update the app WIP/editing state
// once there is a pause in changes (like letting go of a held-down key)
this.dispatchAttributeChange(change);
}
}
}
dispatchAttributeChange(change) {
this.props.onChange(change);
}
render() {
const {value : propsValue, children} = this.props;
const {isEditing, passIsEditing, valuePropName, onChangePropName, singleValue} = this.props;
const {value : stateValue = {}} = this.state;
// Use incoming values from props IF there is no corresponding value
// in local component state. This allows local changes to win out.
const currentValues = defaults({}, stateValue, propsValue);
let valueToPassDown = currentValues;
if(singleValue) {
valueToPassDown = values(currentValues)[0];
}
const editingValue = passIsEditing ? {isEditing} : {};
// Force the child form to re-render itself with these values
const child = React.Children.only(children);
const updatedChild = React.cloneElement(child, {
[valuePropName] : valueToPassDown,
[onChangePropName] : this.onChildChange,
...editingValue
});
return updatedChild;
}
}
export default FormEditWrapper;
import {EDIT_ITEM_ATTRIBUTES} from 'constants/selectedItem';
import {selectedItemSelector, isEditingSelector} from "selectors/entities"
export let editItemAttributes = (newAttributes) => (dispatch, getState) => {
let state = getState();
let isEditing = isEditingSelector(state);
let selectedItem = selectedItemSelector(state);
if(!!selectedItem && isEditing) {
dispatch({type : EDIT_ITEM_ATTRIBUTES, payload : newAttributes});
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment