DEV Community

JacobHsu
JacobHsu

Posted on

Currying

為什麼需要 Currying(柯里化)

簡單說有幾個好處:

One at a Time(一次處理一個參數),每個 function 可以各自處理傳入的參數,藉此提高程式的彈性
也因為拆分成許多片段,所以可以更好地重複利用

例子:

假設今天要計算一個商品的售價,那剛好今天全館商品打 8 折,
在沒有 Currying 的情況下的計算函式會像下面這樣

function calcDiscountPrice(price, discount) {
  return price * discount;
}

calcDiscountPrice(100, 0.8); //80
calcDiscountPrice(80, 0.8); //64
Enter fullscreen mode Exit fullscreen mode

轉成 Curry function 之後:

function calcDiscountPrice(discount) {
  return function (price) {
    return price * discount;
  };
}

const calcWith20PercentOff = calcDiscountPrice(0.8);
calcWith20PercentOff(100); //80
calcWith20PercentOff(80); //64
Enter fullscreen mode Exit fullscreen mode

看出差別了嗎?當我們遇到全館商品都打 8 折的情況下,就不需要每次計算價格時,都還要再帶入一次折扣,一方面減少錯誤機率,另一方面也可以提高程式擴充性

在 React 上的實際應用

在沒有使用 Curry function 的情況下,每個 onChange 事件我們都要寫一個 function 來儲存他的值,那如果我們有一大堆的 input 需要儲存呢?那我們就需要寫很攏長的程式碼來處理它

export default function App() {
  const [user, setUser] = useState({
    name: '',
    age: '',
    phone: ''
  });

  // First handler
  const handleNameChange = (e) => {
    setUser((prev) => ({
      ...prev,
      name: e.target.value
    }));
  };

  // Second handler
  const handleAgeChange = (e) => {
    setUser((prev) => ({
      ...prev,
      age: e.target.value
    }));
  };

  // Third handler
  const handlePhoneChange = (e) => {
    setUser((prev) => ({
      ...prev,
      phone: e.target.value
    }));
  };

  return (
    <>
      <input value={user.name} onChange={handleNameChange} />
      <input value={user.age} onChange={handleAgeChange} />
      <input value={user.phone} onChange={handlePhoneChange} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

轉成 Curry function 之後,我們可以透過傳遞要修改的 field 作為第一個參數來控制要修改的值是哪個,只需要一個 curry function 就可以避免原本需要寫一堆 function 的情況了

export default function App() {
  const [user, setUser] = useState({
    name: '',
    age: '',
    phone: ''
  });

  const handleInputChange = (field) => {
    return (e) => {
      setUser((prev) => ({
        ...prev,
        [field]: e.target.value
      }));
    };
  };

  return (
    <>
      <input value={user.name} onChange={handleInputChange('name')} />
      <input value={user.age} onChange={handleInputChange('age')} />
      <input value={user.phone} onChange={handleInputChange('phone')} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)