654

我一直在阅读一堆React代码,但我看到了一些我不明白的东西:

handleChange = field => e => {
  e.preventDefault();
  /// Do something here
}
4

7 回答 7

1156
于 2015-09-25T17:39:45.867 回答
84

简要地

它是一个返回另一个以简短方式编写的函数的函数。

const handleChange = field => e => {
  e.preventDefault()
  // Do something here
}

// is equal to 
function handleChange(field) {
  return function(e) {
    e.preventDefault()
    // Do something here
  }
}

为什么?

您是否曾经遇到过需要创建可以自定义的功能的情况?或者也许你有一个带有固定参数的回调函数,但是你需要在避免全局变量的同时发送额外的变量?如果您回答,那么这就是如何做到这一点的方法。

例如,我们有一个带有onClick回调的按钮。而我们想要传递id给函数,但是,onClick它只接受一个参数event,所以我们不能这样做:

const handleClick = (event, id) {
  event.preventDefault()
  // Dispatch some delete action by passing record id
}

这是行不通的!

作为解决方案,我们编写了一个函数,该函数返回另一个函数,id其变量范围为不使用任何全局变量:

const handleClick = id => event {
  event.preventDefault()
  // Dispatch some delete action by passing record id
}

const Confirm = props => (
  <div>
    <h1>Are you sure to delete?</h1>
    <button onClick={handleClick(props.id)}>
      Delete
    </button>
  </div
)

功能组成

多个箭头函数也称为“curried 函数”,它们用于函数组合。

// It is just an example, unfortunately, redux does not export dispatch function
import {dispatch, compose} from 'redux'

const pickSelectedUser = props => {
  const {selectedName, users} = props
  const foundUser = users.find(user => user.name === selectedName)
  
  return foundUser.id
}

const deleteUser = userId => event => {
  event.preventDefault()
  dispatch({
    type: `DELETE_USER`,
    userId,
  })
}

// The compose function creates a new function that accepts a parameter.
// The parameter will be passed throw the functions from down to top.
// Each function will change the value and pass it to the next function
// By changing value it was not meant a mutation
const handleClick = compose(
  deleteUser,
  pickSelectedUser,
)

const Confirm = props => (
  <div>
    <h1>Are you sure to delete?</h1>
    <button onClick={handleClick(props)}>
      Delete
    </button>
  </div
)
于 2019-02-07T16:34:23.513 回答
78

一般提示:如果您对任何新的 JavaScript 语法及其编译方式感到困惑,您可以查看Babel。例如,在 Babel 中复制您的代码并选择 ES 2015 预设将给出如下输出

handleChange = function handleChange(field) {
  return function (e) {
    e.preventDefault();
    // Do something here
  };
};

通天塔

于 2018-01-02T04:47:59.410 回答
67

了解箭头函数的可用语法将使您了解它们在“链接”时引入的行为,就像您提供的示例中一样。

当一个箭头函数被编写时没有大括号,有或没有多个参数,构成函数体的表达式被隐式返回。在您的示例中,该表达式是另一个箭头函数。

No arrow funcs              Implicitly return `e=>{…}`    Explicitly return `e=>{…}` 
---------------------------------------------------------------------------------
function (field) {         |  field => e => {            |  field => {
  return function (e) {    |                             |    return e => {
      e.preventDefault()   |    e.preventDefault()       |      e.preventDefault()
  }                        |                             |    }
}                          |  }                          |  }

使用箭头语法编写匿名函数的另一个优点是它们在词法上绑定到定义它们的范围。来自MDN 上的“箭头函数”

函数表达式相比,箭头函数表达式的语法更短,并且在词法上绑定了this值。箭头函数总是匿名的。

考虑到它取自应用程序,这在您的示例中尤其相关。正如@naomik 所指出的,在 React 中,您经常使用this. 例如:

Unbound                     Explicitly bound            Implicitly bound 
------------------------------------------------------------------------------
function (field) {         |  function (field) {       |  field => e => {
  return function (e) {    |    return function (e) {  |    
      this.setState(...)   |      this.setState(...)   |    this.setState(...)
  }                        |    }.bind(this)           |    
}                          |  }.bind(this)             |  }
于 2015-09-25T14:05:43.483 回答
54

可以这样想,每次看到箭头时,将其替换为function.
function parameters在箭头之前定义。
所以在你的例子中:

field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}

然后一起:

function (field) { 
    return function (e) { 
        e.preventDefault(); 
    };
}

从文档

// Basic syntax:
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
   // equivalent to:  => { return expression; }

// Parentheses are optional when there's only one argument:
singleParam => { statements }
singleParam => expression
于 2015-09-25T14:02:45.230 回答
6

它可能并不完全相关,但由于提到的问题反应用例(我一直碰到这个 SO 线程):双箭头函数有一个重要方面,这里没有明确提到。只有“第一个”箭头(函数)被命名(因此在运行时“可区分”),任何后面的箭头都是匿名的,从 React 的角度来看,每次渲染都算作“新”对象。

因此双箭头函数将导致任何 PureComponent 一直重新渲染。

例子

您有一个带有更改处理程序的父组件:

handleChange = task => event => { ... operations which uses both task and event... };

并使用如下渲染:

{ tasks.map(task => <MyTask handleChange={this.handleChange(task)}/> }

然后将 handleChange 用于输入或单击。这一切都有效,看起来非常好。但这意味着任何会导致父级重新渲染的更改(例如完全不相关的状态更改)也会重新渲染您的所有 MyTask,即使它们是 PureComponents。

这可以通过多种方式得到缓解,例如传递“最外”箭头和您将使用的对象或编写自定义 shouldUpdate 函数或返回基础知识,例如编写命名函数(并手动绑定 this...)

于 2019-11-15T14:31:55.270 回答
1

您问题中的示例是 a 的示例,curried function它使用arrow function并具有implicit return第一个参数。

箭头函数在词法上绑定 this ,即它们没有自己的this参数,而是this从封闭范围中获取值

与上述代码等效的是

const handleChange = (field) {
  return function(e) {
     e.preventDefault();
     /// Do something here
  }.bind(this);
}.bind(this);

关于您的示例要注意的另一件事是定义handleChange为​​ const 或函数。可能您将它用作类方法的一部分,并且它使用class fields syntax

所以不是直接绑定外部函数,而是在类构造函数中绑定它

class Something{
    constructor(props) {
       super(props);
       this.handleChange = this.handleChange.bind(this);
    }
    handleChange(field) {
        return function(e) {
           e.preventDefault();
           // do something
        }
    }
}

示例中要注意的另一件事是隐式返回和显式返回之间的区别。

const abc = (field) => field * 2;

上面是一个隐式返回的例子,即。它将 value 字段作为参数并返回field*2明确指定要返回的函数的结果

对于显式返回,您将显式告诉方法返回值

const abc = () => { return field*2; }

关于箭头函数要注意的另一件事是它们没有自己的arguments,但也从父范围继承。

例如,如果您只定义一个箭头函数,例如

const handleChange = () => {
   console.log(arguments) // would give an error on running since arguments in undefined
}

作为替代箭头函数提供您可以使用的其余参数

const handleChange = (...args) => {
   console.log(args);
}
于 2019-02-07T06:08:52.290 回答