5

My React render function eventually renders a set of elements:

data.map((element) => {
  return <Object onChange={this.onObjectChange} />;
});

My question is what is the appropriate way to figure out which object had its onChange method called when I receive the callback?

5 Answers 5

1

The first parameter to the onSubjectChange function will have your event which contains the event information.

Hope that helps!

Sign up to request clarification or add additional context in comments.

6 Comments

Right, but unless the object is aware of its own ordinality in the parent controller and passes that information along, how do I know which object is triggering the callback?
Is the ordinality important? You can access the element by event.target. Get its value, if it has one, by event.target.value.
You should also probably be adding a key for each generated element, since you're doing this in a map operation.
@DaveCooper - Right, so I have a key, is there a way to retrieve the key in the callback?
You should be able to do <Object key={yourKeyHere} onChange={(e) => this.onSubjectChange(yourKeyHere, e)} /> and that should give you everything you need.
|
1

If you can, pass the ID or element to the component you're creating, then pass that reference back to your event handler.

handleObjectChange = id => {
  const object = data.find(id)
}

render() {
  return data.map((element, index) => (
    <Object onChange={this.handleObjectChange} key={element.id} id={element.id} />
  ))
  // or just pass element={element} to track the object itself, why not?
  // after all, every array item's key must be unique
}

In Object...

function change() {
  const { onChange, id } = this.props
  onChange(id)
}

Is your Object closed, or prefer not to add an extra property? You could try wrapping it.

class IdentifiedButton extends Component {
  handleClick = (event, ...args) => {
    const { id, onClick } = this.props
    onClick(event, id, ...args)
  }

  render() {
    const { id, onClick, ...props } = this.props
    return <Button onClick={this.handleClick} {...props} />
  }
}

Comments

0

Wrap the callback in a function and pass an identifier:

data.map(element => <Object onChange={event => this.onObjectChange(element.id, event)} />);

3 Comments

Is there a cleaner way to do this without re-creating every callback on every render call? React is going to see these as new functions (they are) and diff the rest of the tree because of it
In my understanding, the child components will always be rerendered when the parent is changed, independent of whether the props changed or not. Going through the react docs, you can find similar patterns to the one proposed and I'm using it on several occasions in my own apps w/o issues. If the re-rendering of the child components cause performance issues, you could consider implementing shouldComponentUpdate() in the children.
use key, and you could use another property on the component when calling the change... if <object.../> is a custom component, you can have that component pass another property to the function assigned for the event.
0

If rerendering your Object component won't cost you that much I would go with something like this

data.map(element => <Object onChange={() => this.onObjectChange(element)} />);

If Object component is quite heavyweight you better pass element to Object component and then pass it to onChange callback

data.map(element => (
  <Object
    key={element.id} 
    onChange={this.onObjectChange} 
    element={element}
  />
);

class Object extends React.Component {
  handleChange = () => this.props.onChange(this.props.element)

  render(){
    return (
      <input type='text' onChange={this.handleChange} />
    )
  }
}

Comments

0

To avoid creating an anonymous function in every render, you could have a function that creates the handlers like this (I am using Todo App as an example):

createOnChangeEventHandler(todo) {
    const { onToggleClick } = this.props;

    this.handlers = this.handlers || {};
    if (!this.handlers[todo.id]) {
      this.handlers[todo.id] = () => onToggleClick(todo.id);
    }

    return this.handlers[todo.id];
  }

... Then in render()

{todos.map(todo => (<Todo key={todo.id} onClick={this.createOnChangeEventHandler(todo)} />))}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.