Updating a selected object when user changes input

2019-09-10 03:06发布

问题:

When a user clicks a square it becomes currentHtmlObject. I want people to be able to update it's properties in the right sidebar.

I have no idea how to take a single input field and update an object's property that I'm holding in a react-redux state and update the main viewing area DrawingCanvas.

I got kinda close where the info I was entering into the form was activating my reducers and actions. But I couldn't figure out how to distinguish between left and top.

// React
import React from 'react'

export class RightSidebar extends React.Component {
    constructor(props) {
        super(props)
    }

    handleChange(evt) {
        console.log(evt)
        this.props.onUpdateCurrentHtmlObject(evt.target.value)
    }

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

        return (
            <form>
                {this.props.currentHtmlObject.id}
                <div className="right-sidebar">
                    <div className="form-group">
                        <label>Position X</label>
                        <input
                            type="number"
                            name="left"
                            className="form-control"
                            value={this.props.currentHtmlObject.styles ? this.props.currentHtmlObject.styles.left : ''}
                            onChange={this.handleChange.bind(this)} />
                    </div>

                    <div className="form-group">
                        <label>Position Y</label>
                        <input
                            type="number"
                            className="form-control"
                            value={this.props.currentHtmlObject.styles ? this.props.currentHtmlObject.styles.top : ''}
                            onChange={this.handleChange.bind(this)} />
                    </div>
                </div>
            </form>
        )
    }
}

RightSidebar.defaultProps = {
    currentHtmlObject: {
        styles: {
            left: null,
            top: null
        }
    }
}

回答1:

There is no need to distinguish between left and top, let's assume you have an action named update and all it does is to update a selected object's property. Here is what the action method may look like:

updateSelectedObj(id, payload){
    return {
        type: UPDATE_SELECTED_OBJ,
        id: id,
        payload: payload
    }
}

Here is what your event handler might look like in class RightSidebar:

handleChange(evt) {
    // since your top and left input fields have a corresponding name property, evt.target.name will return either `left` or `top`
    store.dispatch(updateSelectedObj({styles:{evt.target.name:evt.target.value}})
}

Here is your reducer:

[UPDATE_SELECTED_OBJ]: (state, action) => {
    // I assume you have a list of objects in the canvas but only one can
    // be selected at a time. and I assume the name of the list is objList
    let selectedObj = state.objList.filter(obj => obj.id == action.id)[0]
    selectedObj = Object.assign({}, selectedObj, action.payload)
    return { objList: state.objList.map(obj => obj.id === action.id? Object.assign({}, obj, selectedObj : obj) }
}


回答2:

I might suggest simplifying the component itself. Sorry for being brief :). I can update w/ more context when I get some time.

This is a stripped down example, but basically thinking of each "number input" as only needing a value and onChange (emits value, not an event).

You would make use of react-redux's connect so that updateObject is a callback accepting the "patch data" to be merged into the currentObject's state.

/**
 * @param currentObject
 * @param updateObject An already bound action creator that accepts payload to "update object"
 */
function SideBar({currentObject, updateObject}) {
  const {styles} = currentObject;
  return (
    <div>
      <NumberInput
        value={styles.left}
        onChange={left => updateObject({left})}
        />
      <NumberInput
        value={styles.top}
        onChange={top => updateObject({top})}
        />
    </div>
  )
}

The connect statement might look something like

const SideBarContainer = connect(
  (state, {objectId}) => ({
    currentObject: _.find(state.objects, {id}),
  }),
  (dispatch, {objectId}) => ({
    updateObject: data => dispatch(
      actions.updateObject(objectId, data)
    )
  })
)(SideBar);

And the actual usage, maybe something like

<SidebarContainer objectId={currentObjectId} />


标签: reactjs redux