Skip to content

Instantly share code, notes, and snippets.

@bradparker
Last active January 6, 2016 03:34
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 bradparker/a6dc37dd6322a57bbf0d to your computer and use it in GitHub Desktop.
Save bradparker/a6dc37dd6322a57bbf0d to your computer and use it in GitHub Desktop.
Generic state container

Must do prop transformation, it needs to add a specific onChange handler to each control.

I dislike this immensly but it'll work with our existing components and is ugly enough to encourage us to move to something like Redux.

// import get from 'lodash/object/get'

const handleControlChange = (onChange, statePath) => (value) =>
  onChange(statePath, value)
  
const StateControl = (Component) => React.createClass({
  contextTypes: {
    onChange: React.PropTypes.func,
    parentState: React.PropTypes.object
  },

  render () {
    const { statePath } = this.props
    const { onChange, parentState } = this.context
    
    return (
      <Component 
        { ...props } 
        value={ get(parentState, statePath) }
        onChange={ handleControlChange(onChange, statePath) } 
      />
    )
  }
})

// import set from 'lodash/object/set'
// import cloneDeep from 'lodash/object/cloneDeep'
  
const GenericStateContainer = React.createClass({
  getInitialState () {
    return {}
  },  

  handleChange (statePath, value) {
    const currentState = cloneDeep(this.state)
    const newState = set(currentState, statePath, value)
    this.setState(newState, () => {
      this.props.onChange(this.state)
    })
  },

  childContextTypes: {
    onChange: React.PropTypes.func,
    parentState: React.PropTypes.object
  },
  
  getChildContext: function() {
    return {
      onChange: this.handleChange,
      parentState: this.state
    }
  },

  render () {
    return (
      <div>
        { this.props.children }
      </div>
    )
  }
})

const ControlledTextInput = StateControl(TextInput)

const StatefulTextInput = React.createClass({
  render () {
    return (
      <GenericStateContainer>
        <ControlledTextInput statePath="value" />
      </GenericStateContainer>
    )
  }
})

const StatefulFieldset = React.createClass({
  render () {
    return (
      <GenericStateContainer>
        <ControlledTextInput statePath="user.first_name" />
        <ControlledTextInput statePath="user.last_name" />
      </GenericStateContainer>
    )
  }
})

Allows us to go:

const ControlledSomething = StateControl(AnythingWithOnChangeAndValueProps)

// ...
<GenericStateContainer>
  <ControlledSomething statePath="something, maybe default to `value`" />
</GenericStateContainer>

Whenever the thing needs state.

It's made a little more convulated because we can have multiple children updating the state of one generic container...

This is in lieu of having "solved" data flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment