在 React 的 onClick 事件处理程序中使用柯里化函数模式的优点和理解

rya*_*yan 9 reactjs

我需要一些帮助来理解 onClick EventHandler 的以下工作代码。详细的解释将有助于理解为什么一个函数在这里返回另一个函数。

const MyComponent = (props) => {
  const onClickHandler = (somearg) => (e) => {
       console.log(`somearg passed successfully is ${somearg}`)
  };

  return (
    <div onClick={onClickHandler(somearg)}>
    </div>
  );
};



export default MyComponent;
Run Code Online (Sandbox Code Playgroud)

Bri*_*son 13

您所指的模式称为“柯里化”。我将在稍后的答案中解释为什么该模式很有用,但首先让我们尝试准确理解发生了什么。

正如您已经确定的,该onClickHandler函数返回一个新函数,除此之外它实际上并没有添加太多内容。现在这意味着当组件呈现时,onClickHandler将立即调用。所以写这个:

<div onClick={onClickHandler('test')}>
Run Code Online (Sandbox Code Playgroud)

最终会返回这个:

<div onClick={(e) => {console.log(`somearg passed successfully is ${somearg}`)}}>
Run Code Online (Sandbox Code Playgroud)

这就是为什么在这里您可以调用 JSX 中的函数,尽管(就像您指出的那样)大多数其他时候您不能这样做。原因是因为返回的函数将实际处理点击。

现在让我们更多地讨论一下为什么这个模式很有用。somearg是模糊的,但我们现在会坚持下去,直到我们得到其他好处。

“currying” 使用 aclosure来“冻结” 的值somearg以供返回的函数使用。查看上面返回函数的示例,somearg似乎不存在。然而,通过关闭,somearg不仅可用,而且还保留 的值'test'

那么为什么要使用这种模式呢?

此模式允许您以其他不可能的方式重用函数。考虑一个用例,其中我们有 2div应该是可点击的。它们都应该做同样的事情,但是能够区分单击了哪个 div 来完成该操作可能会有所帮助。

为了简单起见,我们假设我们有两个div,当单击时我们希望每个都记录他们的订单。

以下是您无需柯里化即可做到这一点的方法:

const Example = () => {
  const onClickHandler1 = (e) => {
    console.log("I am the 1 div.");
  };
  
  const onClickHandler2 = (e) => {
    console.log("I am the 2 div.");
  };
  
   return (
     <div>
      <div onClick={onClickHandler1}>1</div>
      <div onClick={onClickHandler2}>2</div>
     </div>
   );
}

ReactDOM.render(<Example />, document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

上面的效果很好,但是我们的两个函数有很多共享功能。似乎没有必要两者兼而有之。因此,一种解决方案是使用柯里化:

const Example = () => {
  // Converted to longer form so its possible to console.log in the first function
  // It still operates identically to the short-hand form.
  const onClickHandler = (order) => {
    console.log("called with " + order);
    return (e) => console.log("I am the " + order + " div.");
  };
  
   return (
     <div>
      <div onClick={onClickHandler(1)}>1</div>
      <div onClick={onClickHandler(2)}>2</div>
     </div>
   );
}

ReactDOM.render(<Example />, document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

正如您在上面的示例中看到的,onClickHandler组件渲染后就会被调用两次。但第一次order是“冻结”或“关闭”,值为1,第二次值为2

这是一个非常简单的示例,但想象一下您正在循环一组动态数据,每个数据都应返回一个可单击元素。然后,您可以传递索引或 id 或其他变量来标识该元素。

最终,这只是函数重用的一种模式。还有其他方法可以实现相同级别的抽象,例如使用内联函数,但这只是为了解释柯里化及其使用方式。

  • 正是我正在寻找的东西。多谢。现在让我详细阅读柯里化。你避免 DRY 的例子使事情变得清晰和全面。 (2认同)

K.T*_*ess 3

这就是代码第一次运行时发生的情况

const MyComponent = (props) => {
  const onClickHandler = (somearg) => (e) => {
       console.log(`somearg passed successfully is ${somearg}`)
  };

  return (
    <div onClick={onClickHandler('some args')}>
    </div>
  );
};
Run Code Online (Sandbox Code Playgroud)
  1. 当它看到它时,onClick={onClickHandler(1)}它将运行里面的代码onClick

  2. 然后onClickHandler是一个函数,我们传递一个参数 as1并执行它,现在它将返回另一个具有替换值的函数,如下所示。

    (e) => {
        console.log(`somearg passed successfully is 1`); // see args gets replaced with the value.
    }  
    
    Run Code Online (Sandbox Code Playgroud)
  3. 所以现在当我们点击 div 时,上面的函数就会被调用。

为了确保这确实发生了,请参阅下面的演示

const MyComponent = (props) => {
   const onClickHandler = (somearg) => (e) => {
       console.log(`somearg passed successfully is ${somearg}`, new Date().getTime())
   };

   return (
        <div onClick={onClickHandler(new Date().getTime())}>
           div
        </div>
   );
};
Run Code Online (Sandbox Code Playgroud)

你会看到时间不同,这意味着点击时间和函数创建时间不同。


因此,如果您在 onClick 中放置一个函数,因为它会随着代码的执行而执行,并且不会响应您的点击

const MyComponent = (props) => {
    const onClickHandler = (e) => {
       console.log(`somearg passed successfully is`, new Date().getTime())
    };

    return (
       <div onClick={onClickHandler(new Date().getTime())}>
          div
     </div>
    );
};
Run Code Online (Sandbox Code Playgroud)