8

In react native when you have functions that need to run at render and must pass variables, most people suggest that one should use

onPress{() => this.functionName(variable)}

However when working with large lists and complex components you have to optimize your code. Creating a new function for every renderItem in a flatList reduces performance, sometimes hugely so depending on how many functions per renderItem you are creating. So the suggestion is to move from creating a function at render to using a function reference. Like this:

functionName = () => {
   //code
}

onPress={this.functionName}

However I haven't been able to figure out how to pass variables to the function with this method.

If you do this:

onPress={this.functionName(variable}

It will just run the function instantly on component load.

Any ideas?

rt_
  • 1,105
  • 2
  • 15
  • 25

5 Answers5

12

I highly recommend using currying to pass an argument.

Currying is the process in which you separate a function into several functions that each take a single argument. The reason why this method works really well, in this case, is because when you call a function on the render method, the argument passed is automatically the event, so to pass the second argument, the function would have to be curried in order receive the next argument.

You would define your function as such:

functionName = variable => event => {
 //code 
}

You can then go ahead and call your function in the render method:

onPress={this.functionName(variable)}

This is a great article if you would like to learn more: Currying In JS

This method is extremely elegant and useful because it removes the need to wrap your function call in an anonymous function inside the render method of React.

In any other case, you can also do what the official React Docs suggest: React Docs on passing arguments to event handlers

Good luck!

Esther Cuan
  • 367
  • 1
  • 16
  • 2
    Out of curiosity, is using a curried function like that prevents it being created in every render here? I think it does not. I really like currying but for this problem I think it does not solve OP's problem. – devserkan May 31 '18 at 19:48
  • 2
    @devserkan that's correct, this will redeclare the function per render. You can find my answer below for an approach to not redeclare the function without currying: https://stackoverflow.com/questions/50630902/how-to-pass-variables-to-a-function-reference/50633302#50633302 – Alexei Darmin May 31 '18 at 21:53
  • 1
    good to know I was wondering that too. The whole point of this post is avoid re creating a function on render which creates performance issues as well as makes shallow comparison more difficult in some cases. However I do need to understand currying so I'll have a look, thanks for the info! – rt_ May 31 '18 at 22:01
  • Interesting approach using closures through Render Props: https://reactjs.org/docs/render-props.html – Esther Cuan May 31 '18 at 22:25
5

As Esther Cuan suggests, currying is the way to go. Chances are, if you are using dynamically created functions dependent on variable changes. Then the time spent on creating these functions is much less than the time spent rerendering the components every time the variables change. Efficiency wise, the priority should be to minimize variable changes in order to minimize rerenders long before the constant redecleration of functions.

If however you are certain that some functions are consistent, even through rerenders, and do not want them to be redeclared here's how you can achieve that:

class Component extends React.Component {
  functionName = (e) => {
    const { variable } = this.props
    // run logic on the event and variable
  } 

  render() {
    // some code
    onPress={this.functionName}
  }
}

You'll notice with this approach functionName is only declared once.

The performance flaw with this approach is that now you have to create another component (and possibly the lifecycle methods that come with the class) in order to pass variable as a prop, in order to bypass the need for currying.

Alexei Darmin
  • 2,079
  • 1
  • 18
  • 30
  • This kind of discussions always ends with "over-optimization" or not :) I like to avoid using bind or arrow functions at all on handlers. Yes creating new components could be more struggle, writing them, testing them, documenting for them. But hey, I'm just an enthusiast not a pro :) – devserkan Jun 01 '18 at 01:16
1

In your constructor ( or whenever variable is available ), you can pre-bind the function just once:

this.handle_press = this.functionName.bind(this, variable);
ouni
  • 3,233
  • 3
  • 15
  • 21
  • If you do this outside of the constructor doesn't this limit the scope of `this`? – rt_ May 31 '18 at 21:59
  • As long as you point `function.bind(this_obj)` to the correct object, then the correct `this` will appear inside the bound function. Depending on if you are inside a fat arrow function `() => {}` or a function expression `function() {}`, the first argument you supply to `function.bind()` may not necessarily be the same `this` in the currently-running function, if that makes sense. – ouni May 31 '18 at 22:18
1

I'm writing this answer as a learner. So it may need some clarification. When I start to code with JS and React I encounter this problem (thanks to linter) and searched a little bit to learn the best practices. But, I could not find any optimized way to use a variable directly like this. What I do for those situations:

  • If I am rendering list of items, I create its own component as suggested here: react/jsx-no-bind So, pass a callback function and variable, do the job and use this callback function. Yeah, some work here, creating a bunch of handling functions. But, if this is the optimized way, getting my hands dirty is not a problem.

  • If variable is in the state or coming from props, then there is no need to use it on the onClick method like this. In the handler function we can access this variable.

devserkan
  • 16,870
  • 4
  • 31
  • 47
  • You use refs to pass variables? I was under the impression that refs should be not used at all really. I'm sure there are certain circumstances where you have to, but I've only encountered them with textinput methods. Can you should your technique? – rt_ May 31 '18 at 21:57
  • I really thought hard but I couldn't remember where I used it. I am quite sure that I used it but I can't remember how. I've removed that part from my question for not misleading people. Because if there is an input, there could be a state for that, so no need to use refs. And you are right, refs should not be used frequently. At least as React official documentation says: "Don’t Overuse Refs" :) – devserkan Jun 01 '18 at 00:54
1

You can pass variable as value parameter for that element e.g.

<button
 value={variable}
 onPress={this.functionName}>
 click
</button>

Then you can "pick up" that variable as:

fuctionName = e => {
 const yourVariable = e.target.value;
 // now you have access to your variable through the const declaration
}
KT-mongo
  • 2,044
  • 4
  • 18
  • 28