3

I just read this highly enlightening article today:

https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f

The article uses the following example to show how to pre-bind functions to prevent components from needless re-rendering:

class App extends PureComponent {
  constructor(props) {
    super(props);
    this.update = this.update.bind(this);
  }
  update(e) {
    this.props.update(e.target.value);
  }
  render() {
    return <MyInput onChange={this.update} />;
  }
}

This makes sense, though I can't figure out how to pass an argument to the function without using bind or () => this.myFunc('some arg'). Example:

  <div>
    <TextField
      floatingLabelText="Email"
      required
      value={this.state.email}
      onChange={e => this.setState({email: e.target.value})}
    />
  </div>
  <div>
    <TextField
      type="password"
      floatingLabelText="Password"
      required
      value={this.state.password}
      onChange={e => this.setState({password: e.target.value})}
    />
  </div>

I'm not sure how to refactor this to not use binding. I've got this class method as a starting point:

_textChange(field, value) {
  this.setState({
    [field]: value,
  });
}

But I'm not sure how to pass arguments into it from the onChange prop and follow the suggestions laid out by the article above.

Additionally, I have code that I've refactored into this:

export default OrgList = (props) => {
  const orgs = props.orgs.map((org) => {
    const addEditOrg = props.onAddEditOrg.bind(null, org, 'edit');
    const deleteOrg = props.onDeleteOrg.bind(null, org);

    return <TableRow key={org._id}>
      <TableRowColumn>{org.name}</TableRowColumn>
      <TableRowColumn>
        <IconButton onTouchTap={addEditOrg}>
          <ModeEdit />
        </IconButton>
        [...]

But I'm not sure if those pre-bound functions are in the right place. Do they need to be totally outside the component?

1

2 Answers 2

4

I use name attribute for this. Example:

handleChange(e) {
  this.setState({[e.target.name]: e.target.value})
}

<TextField
  name="email"
  onChange={this.handleChange}
/>

<TextField
  name="password"
  onChange={this.handleChange}
/>
Sign up to request clarification or add additional context in comments.

Comments

1

Unfortunately, you have to create separate methods which set email/password.

linkState is noop here, since it always returns different object. Same apply to higher order function (returning anonymous function as handler).

I would end up with this code. Event handlers are same, no unnecessary rendering is triggered this way.

setEmail(e) {
  this.setState({email: e.target.value})
}

setPassword(e) {
  this.setState({password: e.target.value})
}

<TextField
  onChange={this.setEmail}
/>

<TextField
  onChange={this.setPassword}
/>

10 Comments

Wow, that's highly unfortunate. What if I had 20 fields? It's a shame we have to choose between performance and code legibility.
PS: if you wouldn't mind, I added on a follow-up question above.
Yep, that's unfortunate. And when we have 20 fields like this,... you have to decide. If it's not causing performance problems, go with linkState. For elements which don't contain any children elements (input, ...) it should be no problem. To answer your extended question: yes. They should be bound outside of your component. Think of stateless function component as a render method in ES6 class or React's stateful component.
But in the second code example (with the map function), it's impossible to bind those functions outside the component, because the arguments passed depend entirely on the org object which changes on each iteration within map.
Why not just use shouldComponentUpdate and stick with binding functions in the event handlers like before?
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.