Search code examples
javascriptreactjsreact-nativelodash

JS Lodash - How to refactor a curried function with _.curry()?


Introduction

I had this code:

import React, { Component } from 'react';
import { View, Button } from 'react-native';

export default function App() {
  const myMethod = (name) => {
    console.log(`Hello ${name}!`);
  };

  return (
    <View>
      <Button onPress={() => myMethod("Victor")}>
        Press me!
      </Button>
    </View>
  );
}

Then, I decided to refactor it using curried functions. Like this:

import React, { Component } from 'react';
import { View, Button } from 'react-native';

export default function App() {
  /**
   * @param {string} name - The name.
   * @returns {Function} Curried function.
   */
  const myMethod = (name) => {
    return () => {
      console.log(`Hello ${name}!`);
    }
  };

  return (
    <View>
      <Button onPress={myMethod("Victor")}>
        Press me!
      </Button>
    </View>
  );
}

As you can see, the way I am invoking the method has changed, as I am returning a function instead of void in myMethod. That's cool, it works... every time the button is pressed, my method is invoked.

Check this snack: https://snack.expo.dev/Rx3iBVT_W

Problem

Now, I am trying to refactor the refactored code using curry from Lodash.

This is what I have tried:

import React, { Component } from 'react';
import { View, Button } from 'react-native';
import { curry } from 'lodash';

export default function App() {
  const myMethod = curry((name) => {
    console.log(`Hello ${name}!`);
  });

  return (
    <View>
      <Button onPress={myMethod("Victor")}>
        Press me!
      </Button>
    </View>
  );
}

But for some reason, this is not working. The method is not executed every time the button is pressed... Instead, it is invoked when the component mounts.

How can I solve it?

Check this snack: https://snack.expo.dev/bna6cLkhp


Solution

  • I'm afraid you've misunderstood currying (which is easily done, and lots have — including myself).

    Then, I decided to refactor it using curried functions. Like this:

    That code doesn't curry a function. It just creates a closure over the name parameter. That's called partial application.

    Lodash's curry does currying instead. The function it returns will do its work as soon as all of its parameters are satisfied — as soon as you provide it the name it needs, in your case. So it does its work when you do myMethod("Victor"), not later:

    const myMethod = _.curry((name) => {
        console.log(`Hello ${name}!`);
    });
    
    myMethod("Victor");
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

    If you want it to wait for a second argument, you need to tell curry that your function requires two arguments, and then provide it with two arguments (one at a time in this case):

    const myMethod = _.curry((name) => {
        console.log(`Hello ${name}!`);
    }, 2);
    // ^−−−−−−− tells `curry` this function needs two arguments, even
    //          though the function itself says it only needs 1
    
    console.log("Supplying the first argument");
    const fn = myMethod("Victor");
    console.log("Supplying the second argument");
    fn({}); // <== Note the argument, we need to supply one
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

    That works because we've told curry your function needs to have two arguments, and myMethod("Victor") only gives it one, so it returns another function that will do its work when you give it the other argument. onPress will give it a second argument because it passes the event object to it.

    Check out the curry documentation for more examples.

    That said, I wouldn't use curry here. You're not really currying, so it's a bit of a misuse. I'd use your partial application solution, if I had to bake the name into the function at all.