Yomguithereal / baobab-react

React integration for Baobab.
MIT License
310 stars 38 forks source link

Accessing cursors from context no longer works in 2.0.0 #108

Closed demux closed 8 years ago

demux commented 8 years ago

I noticed that in higher-order.js cursors are no longer provided in context. What is the proposed alternative?

Yomguithereal commented 8 years ago

Hello @demux. Indeed, I decided not to provide cursors through context anymore because I believe this is kind of a bad practice to do so. You should rely on actions instead if what you want is to edit the tree. Alternatively, you can still access the tree through context if you ever need it. What's more, this should provide some performance benefits not to pass the cursors.

demux commented 8 years ago

I solved my problem by using dispatch on an instance method. Creating an action for setting any path in the tree seemed kinda bonkers.

import React, {Component} from 'react'
import {branch} from 'baobab-react/higher-order'
import PropTypes from 'baobab-react/prop-types'

import Textarea from 'react-textarea-autosize'

export class BaseBoundInput extends Component {
  constructor(props) {
    super(props)
    this._setValue = this._setValue.bind(this)
    this.setValue = this.setValue.bind(this)
    this.onChange = this.onChange.bind(this)
  }

  // componentDidUpdate() {
  //   console.log(`BoundInput [${this.getFullPath().join(', ')}] Updated`)
  // }

  onChange(e) {
    this.setValue(e.target.value)
  }

  setValue(value) {
    this.props.dispatch(this._setValue, value)
  }

  _setValue(tree, value) {
    tree.select(this.getFullPath()).set(value)
    tree.commit()
  }

  getFullPath() {
    return (this.props.basePath || []).concat(this.props.path || [])
  }

  inputProps() {
    const {value, type, ...props} = this.props

    return {
      type: (type && type !== 'textarea') ? type : 'text',
      name: this.getFullPath().join('.'),
      value: value || '',
      onChange: this.onChange,
      ...props
    }
  }

  renderInput() {
    return this.props.type === 'textarea'
           ? <Textarea {...this.inputProps()} />
           : <input {...this.inputProps()} />
  }

  render() {
    const {label} = this.props

    if(label === undefined) {
      return this.renderInput()
    }

    return <div className="field">
      <label>{label}</label>
      {this.renderInput()}
    </div>
  }
}

export const curriedBranch = branch((props) => {
  return {
    value: BaseBoundInput.prototype.getFullPath.call({props})
  }
})

const BoundInput = curriedBranch(BaseBoundInput)

export default BoundInput
Yomguithereal commented 8 years ago

It may seem overkill to use actions but every recent state management option with React do so to keep a sane architecture (Flux & redux notably). People like to centralize in their own files all the modification logic of the state rather than having to find the related components.

demux commented 8 years ago

I use actions for everything else, but in this case it would kinda be like going home from work to get sunglasses just to cross the street.

Yomguithereal commented 8 years ago

In this particular case, I usually tend to use function composition either at component or action level not to have to write this every time.