"this" 未定义提交后的表单,但未定义反应中的按钮

"this" is undefined for a form after submit but not for button in react

注意:这个问题不是询问如何绑定事件处理程序。就是问为什么,without bindingthisButtononClick(指的是一个对象)和FormonSubmit(它是 undefined)。

完整问题:

如果我不在构造函数中绑定方法,我试图检查方法中的 this 对象会发生什么情况。我发现 Form 和 Button 的结果不同。下面是我的代码,以便更好地理解:

const {Button, Form, Input} = Reactstrap;

class Test extends React.Component{
    constructor(props){
        super(props);
    }

    handleClick(){
        console.log(this);    //This gives complete information about the Button object and its properties 
    }

    handleSubmit(e){
        console.log(this);    //This one is undefined 
        e.preventDefault();
    }

    render(){
        return(
            <React.Fragment>
                <Form onSubmit={this.handleSubmit} >

                <Input type="submit" name="submit">Submit</Input>

                </Form>

                <Button name="butt1" onClick={this.handleClick}>Click</Button>
            </React.Fragment>
        );
    }
}

ReactDOM.render(<Test />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/8.4.1/reactstrap.min.js"></script>

我检查过我的问题与 不同,因为在提供的问题中,发问者想为组件绑定 this,而在我的问题中,我想 this 引用 Form,就像引用 Button 一样。

我试图找到这种不同行为的原因,但我没能成功。你们能否给出相同的原因并提出一种方法,以便 handleSubmit 中的 this 开始引用 Form 对象?

编辑 这是我认为的原因,请确认是否正确

由于提交处理程序是用表单定义的,而不是为提交按钮定义的,这就是为什么 this 未定义的原因,因为点击的是提交,而不是表单。我想我需要一些冒泡捕获之类的东西。

这似乎是 Reactstrap 中的错误(或至少是不一致)。对于正常的 formbutton 元素,React 始终调用没有特定 this 值的处理程序¹(所以在这个例子中,因为 class 代码总是处于严格模式,我们看到this 在调用中设置为 undefined:

class Test extends React.Component{
    constructor(props){
        super(props);
    }

    handleClick() {
        console.log(typeof this);
    }

    handleSubmit(e) {
        console.log(typeof this);
        e.preventDefault();
    }

    render(){
        return(
            <React.Fragment>
                <form onSubmit={this.handleSubmit} >

                <input type="submit" name="submit" value="Submit" />

                </form>

                <button type="button" name="butt1" onClick={this.handleClick}>Click</button>
            </React.Fragment>
        );
    }
}

ReactDOM.render(<Test />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>


But I have read that this inside normal functions is resolved dynamically i.e. who called it.

这是不正确的。对于普通函数和方法,调用中 this 的值由调用者确定。所以我上面说的是 React 做一件事而 Reactstrap 做另一件事。

您可能会想到 DOM 以及它处理事件处理程序的方式,这与 React 和(显然)Reactstrap 都不同。 DOM 调用您的处理程序并将 this 设置为处理程序附加到的元素。因此,如果您将处理程序附加到 button 并且处理程序是普通函数或方法,则当 DOM 调用处理程序时,this 将引用 button .这只是 DOM 所做的事情。反应没有。 Reactstrap 显然取决于它是什么处理程序或它是什么类型的元素(这可能是一个错误)。

this question's answers and in this old post on my anemic little blog 中有关 this 的更多信息。


您在评论中说:

One more thing that I want to ask is how to use your code without strict mode, because it doesn't specify strict mode to be used

该示例中的代码处于严格模式的原因有两个:

  1. 正如我之前在回答中提到的,它位于 class 结构中。 class 中的代码始终是严格的。 (JavaScript 模块中的代码也是如此。)
  2. 它使用 Babel 来转换 JSX,默认情况下 Babel 在它输出的代码中打开严格模式。

因此,要查看松散模式下会发生什么,您必须不使用 class(足够简单)并且不使用 Babel,或者至少告诉 Babel 不要打开严格模式。我不知道是否有办法告诉 Babel 不要在 Stack Snippets 中使用严格模式(当你在现实世界中使用它时有一种方法),但幸运的是,你没有 将 JSX 与 React 一起使用,很方便。可以直接使用React.createElement

const {createElement} = React;

function Test() {
    function handleClick() {
        console.log(this === window);
    }

    function handleSubmit(e) {
        console.log(this === window);
        e.preventDefault();
    }

    return createElement(React.Fragment, {
        children: [
            createElement("form", {
                onSubmit: handleSubmit,
                children: [
                    createElement("input", {
                        type: "submit",
                        name: "submit",
                        value: "Submit"
                    })
                ]
            }),
            createElement("button", {
                type: "button",
                name: "butt1",
                onClick: handleClick,
                children: ["Click"]
            })
        ]
    });
}

ReactDOM.render(createElement(Test), document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

请注意,回调中的 this 现在是全局 this,因为当您在松散模式下调用没有特定 this 值的普通函数或方法时,this 在回调中是全局 this(在浏览器上为 window)。


¹ React 的 event documentation 甚至涵盖了这一点,尽管它实际上说的是 this 将是 undefined。只有在严格模式下才是正确的。从技术上讲,在不使用严格模式的情况下使用 React 是可行的——但不是一个好主意。