3

I have the following situation

export default class MyComponent extends Component {

    myFunc = dynamicKey => {
        // do something with the dynamic key
    }

    render() {

        return (
            <Foo>
                <button onClick={e => this.myFunc(someDynamicKey1)} /> 
                <button onClick={e => this.myFunc(someDynamicKey2)} /> 
                <button onClick={e => this.myFunc(someDynamicKey3)} /> 
                {/* ... */}
            </Foo>
        )
    }
}

Which is a very common case, but It isn't good because on every render it's creating that arrow function.

So as a walkaround, I made a function that returns another function with that key.

export default class MyComponent extends Component {

    myFunc = dynamicKey => e => {
        // do something with the dynamic key
    }

    render() {

        return (
            <Foo>
                <button onClick={this.myFunc(someDynamicKey1)} /> 
                <button onClick={this.myFunc(someDynamicKey2)} /> 
                <button onClick={this.myFunc(someDynamicKey3)} /> 
                {/* ... */}
            </Foo>
        )
    }
}

Now I'm not creating a new function on every render but I'm calling a new function on every render.

Now I'm not sure which one to use. Is calling a function on every render a bad practice? Should I use a arrow function?

Vencovsky
  • 28,550
  • 17
  • 109
  • 176
  • You might wanna read this question https://stackoverflow.com/questions/29810914/react-js-onclick-cant-pass-value-to-method – Atin Singh Dec 24 '19 at 13:20
  • 1
    The rendering itself (updating the DOM) is much more time consuming in comparison with such differences in the code. And it might well be that the JS parser optimises this code anyway, so the in-place callback function is actually not created every time. So I would say: don't worry about this, unless you can measure the difference in time. – trincot Dec 24 '19 at 13:21
  • @AtinSingh that is a very good question to read, but unfortunately not my case. I can't make a child object because is just a button. It would be weird to create a wrapper that only handles that. – Vencovsky Dec 24 '19 at 13:24
  • @AtinSingh I just edited my question so it looks more real and show better what situation I'm in. – Vencovsky Dec 24 '19 at 13:34
  • I'm confused as to why you have three button clicks to the same function in that component? Couldn't you just have the one function and then dynamically pass the key to that function? If you could explain that would help. The way I normally do it is like so: onClick={this.myFunc.bind(this)}. That way I'm not creating an arrow function. – Michael Dec 24 '19 at 14:13
  • `Couldn't you just have the one function and then dynamically pass the key to that function?` I'm doing that. – Vencovsky Dec 24 '19 at 14:20

2 Answers2

0

When using the curried function, you can use its closure on the current scope.

export default class MyComponent extends Component {
    state = {
      counter: 42
    }
    myFunc = dynamicKey => e => {
        // closure on the specific this.state.counter value at time of render.
    }
}

While returning a new function on every render, its closure is on the recent scope

export default class MyComponent extends Component {
    state = {
      counter: 42
    }
    myFunc = dynamicKey => {
        // closure on this.state.counter value
    }
}

Therefore, it depends on what is the use case. Ask yourself if the function needs a specific value or the recent one.

Note: if on every render the functions re-declared, it becomes a "difference between function and curried one" question, and for React it doesn't matter as both functions bodies will be executed. So only by memoizing the function (don't call the function with it is called with the same parameters), you can get any noticeable differences.

Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
  • This won't impact on anything because when any state changes, it will call the function again and it will be the same value. It will never have a case where is state is out of sync in my function. `value at time of render` will always be the same as `value`. – Vencovsky Dec 24 '19 at 13:43
  • If it is called again with the same parameters its indeed has no effect and they both act the same (you can check the Wikipedia page, **it becomes a question about what is the difference between function and curried one**), but if you memoize the function you can achieve the closure above. – Dennis Vash Dec 24 '19 at 13:47
-2

You can cache your event handlers.

class SomeComponent extends React.Component {
    // Each instance of SomeComponent has a cache of click handlers
    // that are unique to it.
    clickHandlers = {};
    // Generate and/or return a click handler,
    // given a unique identifier.
    getClickHandler = (key) => {
     // If no click handler exists for this unique identifier, create one.
     if (!this.clickHandlers[key])){
      this.clickHandlers[key] = () => alert(key);
      }
       return this.clickHandlers[key];
    }

   render() {
    return (
        <ul>
          {this.props.list.map(listItem =>
          <li key={listItem.text}>
          <Button onClick={this.getClickHandler(listItem.text)} />
        </li>
          )}
       </ul>
     );
  }
}

see the following article

If you use React hooks then:

const Button = props => {
  const onClick = React.useMemo(() => {
  alert(listItem.text)
   }, [listItem.text]);
  }
 return <button onClick={onClick}>click</button>
}

If your function does not depend on your component (no this contexts), you can define it outside of the component. All instances of your component will use the same function reference, since the function is identical in all cases.

In contrast to the previous example, createAlertBox remains the same reference to the same location in memory during every render. Button therefore never has to re-render.

While Button is likely a small, quick-to-render component, you may see these inline definitions on large, complex, slow-to-render components, and it can really bog down your React application. It is good practice to simply never define these functions inside the render method.

If your function does depend on your component such that you cannot define it outside the component, you can pass a method of your component as the event handler:

In this case, each instance of SomeComponent has a different alert box. The click event listener for Button needs to be unique to SomeComponent. By passing the createAlertBox method, it does not matter if SomeComponent re-renders. It doesn’t even matter if the message prop changes! The address in memory of createAlertBox does not change, meaning Button does not have to re-render, and you save processing time and improve rendering speed of your application.

For dynamic functions

In this case, you have a variable number of buttons, making a variable number of event listeners, each with a unique function that you cannot possibly know what is when creating your SomeComponent. How can you possible solve this conundrum?

Enter memoization, or what may be easier to refer to as simply, caching. For each unique value, create and cache a function; for all future references to that unique value, return the previously cached function.

gadi tzkhori
  • 574
  • 2
  • 13