Reactjs的setState箭头函数语法

19

根据React文档,我们可以使用两种方式来进行setState操作,一种是对象语法,另一种是函数语法,如下所示:

this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

我的理解是箭头函数的语法类似于() => {},其中箭头=>后面跟着花括号,但是根据示例,它使用的是圆括号而不是花括号。
这些语法()=>{}()=>({})之间有什么区别?
按照文档尝试的示例代码在handleClick函数中使用this.setStage(prevStage=>({}))语法时可以工作,如果将其更改为this.setState(prevStage=>{}),它将无法切换按钮值。
以下是可工作的代码:
class Toggle extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isToggleOn : true
        }
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        this.setState(prevState => ({
            isToggleOn: !prevState.isToggleOn
          }));
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>
                    {this.state.isToggleOn ? 'ON' : "OFF"}
                </button>
            </div>
        );
    }
}

实际上,当()被移除时,没有JavaScript错误!!! - Chandre Gowda
4个回答

17

这里需要考虑两个主要问题:

  1. 箭头函数是如何工作的?

  2. 当将function作为参数传递给setState时,它期望什么?

答案:

  1. Arrow functions can return a value implicitly or explicitly.
    When there is no function body (no curly brace {}) then you are returning implicitly:

    const x = () => 'we are returning a string here';  
    

    When we use a function body, we need to use the return key word:

    const x = () => {
      return 'another string returned'
    };
    

    There is another option to return something without the return key word, you can wrap the curly brace with parentheses () and this will signal the engine that the curly brace are not a function body but an object, this is considered as creating an expression:

    const x = () => ({myKey: 'some string'});
    

    This is similar as we usually do with function expressions.
    Especially with IIFE (Immediately Invoked Function Expression) :

    (function() {
        //some logic...
    })();  
    

    If we will not return anything, then the function will just return undefined.

  2. As for setState, when you pass a function as a parameter, it expect that this function will return an object.
    When your function didn't return anything (as stated above) it actually returned undefined.
    JavaScript won't yield an error as this is not an error. its just a function that returns nothing (undefined).

这是一个运行示例,演示了您的代码不带括号的情况:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isToggleOn: true
    }
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => {
      return { // we must return an object for setState
        isToggleOn: !prevState.isToggleOn
      } 
    });
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          {this.state.isToggleOn ? 'ON' : "OFF"}
        </button>
      </div>
    );
  }
}


ReactDOM.render(<Toggle />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

编辑
作为对您评论的跟进

我希望JavaScript在我们返回键值对时抛出错误,而不是用括号将其括起来,即() => {key:value}。这意味着它试图返回'key:value'而不是对象,并且这应该是JS错误,但它没有抛出任何错误。如果我理解有误,请纠正我

它不会返回键值对,它是一个返回undefined的"void"函数。
请看下面运行的代码片段:

const x = () => {myKey: 'myValue'};
const y = x();
console.log(y);

编辑#2
针对您其他评论的跟进(在我看来基本上是一个完全不同的问题)。

let y = function() {'abc':1} - 语法错误,let y = function(){abc:1} 和 let y = function(){ return {'abc':1} } - 没有错误,第一个(语法错误)我们试图将1分配给字符串abc,这与第三个示例(没有错误)相同,第二个示例将1分配给abc-当没有引号时有效。请解释这3个样本之间的区别以及为什么第一个失败而不是第二个示例

好的,这变得有趣了。

其中第一个(语法错误)我们试图将1分配给字符串abc...

不是这样。
我们正在尝试创建一个label:,但标签不能是字符串!
变量也不能是字符串 - var 'x' = 1

这是JavaScript中的有效语法:

const y = function(){b:2};

What we are doing here is creating a label: named a and this label has an expression of 1 (we are not doing anything with this label.).

const x = () => {a:1};
const y = function(){a:1};

此语法无效:

const y = function() { 'a': 1 };

This is not valid because labels can't start with a string:

const x = () => { 'a': 1 };
const y = function() { 'a': 1 };

再次强调,这不是一个键:值对,花括号是函数的主体


让 x = () => { 'a' : 1 };让 y = () => ( { 'a' : 1 });如果我们在文件中只尝试上述两个表达式,我将会得到 JavaScript 错误,即 SyntaxError: Unexpected token : ,但是当我们在花括号周围没有括号时,示例代码不会抛出此语法错误。 - Chandre Gowda
这与箭头函数无关。如果你写成 let y = function(){'a':1},仍会得到相同的错误。如果去掉引号:let y = function(){a:1},将不会抛出错误,但是它会返回 undefined - Sagiv b.g
let y = function(){ 'abc':1 } - 语法错误,let y = function(){abc:1}let y = function(){ return {'abc':1} } - 没有错误。在第一个(语法错误)中,我们试图将1分配给字符串abc,这与第三个示例(没有错误)相同,而第二个示例将1分配给abc-仅适用于没有引号的情况。请解释这3个样本的区别以及为什么第一个失败而第二个示例不失败。 - Chandre Gowda
@ChandreGowda,我对答案进行了另一次更新,希望这样更清晰明了。 - Sagiv b.g
@Sagivb.g 我有一个与此相关的不同问题,如果我们使用函数语法来setState,那么state是否会被React自动传递?例如 this.setState((state)=>({counter: state.counter+1})) - Altair21
1
@Altair21 是的,最新的状态。 - Sagiv b.g

11

后来我参考了 MDN,在高级语法部分找到了细节。如果你想隐式返回对象,那么我们需要将其括在 () 中,这回答了我的问题。

// 在函数主体上加上括号,以返回对象字面表达式:

params => ({foo: bar})


5
括号 () 会生成一个表达式。 - Sagiv b.g
@Sagivb.g,你有什么想法,为什么在示例代码的handleClick函数中没有添加()返回键值对时,JavaScript没有出现错误? - Chandre Gowda
箭头函数会隐式返回,但是当你为函数创建一个主体(使用 {})时,你需要显式地返回 { return {...} }。如果你需要更多的信息和文档链接,请让我知道,我会发布一个适当的答案。 - Sagiv b.g
@Sagivb.g 我认为当我们返回只有键值对而没有括号时,即 () => {key:value},JavaScript应该会抛出错误。这意味着它试图返回'key:value'而不是对象,因此应该会出现JS错误,但实际上并没有抛出任何错误。如果我理解有误,请纠正我。 - Chandre Gowda
它实际上没有返回键值,而是返回了 undefined。这在 JavaScript 中不是一个错误。请查看我的答案以获得更清晰的解释。 - Sagiv b.g

1
简单的答案是:
()=>({})

也等同于

()=> {
   return {}
 }

返回一个空对象,这里括号中的{}表示return。同时你知道我们必须将对象传递给setState,因此我们在{}之间插入任何想要设置为状态的内容。

()=>({any thing you want to set to state})

0

如果你只写 () => {},那么这明确表示该函数不仅仅返回某些内容。

例如:

const logAndReturn = (val) => {
  console.log(val)
  return val
}

但是假设你有一个函数,它接受参数并基于这些参数返回一个对象。

const createUser = (x) => { 
  prop: x
}

这将会提示一个错误,因为它的翻译是:

function createUser(x) { 
  prop:x 
}

使用括号时,您仍在使用箭头函数的默认返回值。

const createUser = (name, email) => ({})
function createUser(name, email) { return {} )

(x) => { prop : x } 这段代码样例中没有括号包围花括号也没有引发错误,这造成了一些混淆。 - Chandre Gowda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接