箭头函数和bind在react中的应用

2023-11-23 09:16 孙水迪 424

一、绑定事件

在vue中,基本不需要关心函数在执行时的上下文this指针问题,这与react有很大的不同,如果在编写react组件的时候,忽略了this指针的问题,就会出现下面这个情况

class LoggingButton3 extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'Hello'};
    }

    handleClick(){
        console.log(this);
        this.setState({
            name:"World!"
        })
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>
                    Click me
                </button>
                <h1>{this.state.name}</h1>
            </div>
        );
    }
}
function App(prop) {
    return (
        <LoggingButton/>
    );
}

在上图代码运行后,会出现如下页面

但只要点击按钮,就会出现报错

可以看到,打印的this是undefined,这就是没有给函数绑定this造成的

绑定this的方法有两种,其中区别后续会提到

  • 在定义函数时使用箭头函数
handleClick=()=>{
    console.log(this);
    this.setState({
        name:"World!"
    })
}
  • 在绑定事件时在函数后面调用bind(this)
<button onClick={this.handleClick.bind()}>
    Click me
</button>

二、父子组件传递函数时的this绑定

有时候,子组件会需要改变父组件的状态,这时候,父组件给子组件传递一个函数,由子组件调用该函数,会是一个可行的选择

使用了箭头函数绑定this

如下代码

class LoggingButton2 extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'Hello'};
    }
    render() {
        return (
            <div>
                <button onClick={this.props.clickProp}>
                    Click me2
                </button>
                <h1>{this.state.name}</h1>
            </div>

        );
    }
}

class LoggingButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'world?'};
    }

    handleClick=()=>{
        console.log(this);
        this.setState({
            name:"world!"
        })
    }

    render() {
        return (
            <div>
                <LoggingButton2 clickProp={this.handleClick}/>
                <h1>{this.state.name}</h1>
            </div>
        );
    }
}


function App(prop) {
    return (
        <LoggingButton/>
    );
}

通过点击后发现,控制台上打印的是父组件的this,也成功将"Hello world?"修改为"Hello world!"

改成bind方式绑定

修改父组件代码

class LoggingButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'world?'};
    }

    handleClick(){
        console.log(this);
        this.setState({
            name:"world!"
        })
    }

    render() {
        return (
            <div>
                <LoggingButton2 clickProp={this.handleClick.bind(this)}/>
                <h1>{this.state.name}</h1>
            </div>
        );
    }
}

通过点击按钮,可以发现,运行效果和箭头函数绑定时一样

将bind函数移到子组件里

如下代码修改父子组件

class LoggingButton2 extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'Hello'};
    }
    render() {
        return (
            <div>
                <button onClick={this.props.clickProp.bind(this)}>
                    Click me2
                </button>
                <h1>{this.state.name}</h1>
            </div>

        );
    }
}

class LoggingButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'world?'};
    }

    handleClick(){
        console.log(this);
        this.setState({
            name:"world!"
        })
    }

    render() {
        return (
            <div>
                <LoggingButton2 clickProp={this.handleClick}/>
                <h1>{this.state.name}</h1>
            </div>
        );
    }
}

点击按钮后会发现,handleClick函数虽然在父组件中,但是其this被绑定到了子组件里

父子组件同时使用bind函数

如下代码

class LoggingButton2 extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'Hello'};
    }
    render() {
        return (
            <div>
                <button onClick={this.props.clickProp.bind(this,2)}>
                    Click me2
                </button>
                <h1>{this.state.name}</h1>
            </div>

        );
    }
}

class LoggingButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'world?'};
    }

    handleClick(a,b){
        console.log(this)
        console.log(a)
        console.log(b)
        this.setState({
            name:"world!"
        })
    }

    render() {
        return (
            <div>
                <LoggingButton2 clickProp={this.handleClick.bind(this,1)}/>
                <h1>{this.state.name}</h1>
            </div>
        );
    }
}

运行结果如图

父组件成功绑定了this和第一个入参a,子组件的this绑定无效,但是第二个入参b也绑定成功了

这说明bind绑定的this是无法被bind再次覆盖的

父组件使用箭头函数,子组件使用bind

如下代码

class LoggingButton2 extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'Hello'};
    }
    render() {
        return (
            <div>
                <button onClick={this.props.clickProp.bind(this,2)}>
                    Click me2
                </button>
                <h1>{this.state.name}</h1>
            </div>

        );
    }
}

class LoggingButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'world?'};
    }

    handleClick=(a)=>{
        console.log(this)
        console.log(a)
        this.setState({
            name:"world!"
        })
    }

    render() {
        return (
            <div>
                <LoggingButton2 clickProp={this.handleClick}/>
                <h1>{this.state.name}</h1>
            </div>
        );
    }
}

运行结果如图

箭头函数成功绑定了this,bind函数绑定this失败,但是第一个入参a绑定成功了

结论

在给函数绑定this时,无法覆盖已经绑定的this

三、2种方式绑定this的区别

使用箭头函数会立刻确定绑定的this对象,接下来无论怎么传递这个函数,都不会改变this,效果非常明确

使用bind绑定this时,如果需要传递这个函数,一旦先bind了,后续收到这个函数时再bind会失效,如果没有先bind,后续收到这个函数没有bind会报错,同时不同地方bind,指向的this也会不一样,其运行会根据不同的编码产生不同的效果

因此,在定义绑定函数时,推荐优先使用箭头函数来绑定this

bind方式虽然比较灵活,效果多样,但这并不适合绑定函数,绑定函数需要明确的效果

如果一个函数需要在不同的组件里有不同的效果,比较好的方式应该是将其整合出来单独放到一个文件里以供不同组件调用

四、bind在绑定this之外的应用

虽然在绑定this时,箭头函数其效果明确的优点可以避免很多不必要的问题,但是箭头函数无法给同一个函数传递不同的参数,因此,可以同时使用箭头函数和bind,这样子既解决了绑定this的问题(bind无法覆盖箭头函数的this),又可以给函数传递参数

代码如下

class LoggingButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'Hello world!'};
    }

    handleClick=(a)=>{
        console.log(this)
        console.log(a)
        this.setState({
            name:a
        })
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick.bind(null,1)}>
                    Click me1
                </button>
                <button onClick={this.handleClick.bind(null,2)}>
                    Click me2
                </button>
                <button onClick={this.handleClick.bind(null,3)}>
                    Click me3
                </button>
                <h1>{this.state.name}</h1>
            </div>
        );
    }
}

依次点击3个按钮,文本会变成1,2,3,而this依然指向LoggingButton

在这里,bind的第一个参数无论输入什么都不会生效

五、连续使用bind

如下代码,如果bind后继续bind

class LoggingButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name:'Hello world!'};
    }

    handleClick=(a,b,c,d,e)=>{
        console.log(this)
        console.log(a)
        console.log(b)
        console.log(c)
        console.log(d)
        console.log(e)
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick.bind(null,1).bind(null,2,3).bind(null,4)}>
                    Click me1
                </button>
                <h1>{this.state.name}</h1>
            </div>
        );
    }
}

运行结果如下

连续bind时,除了bind的第一个用于绑定this的参数外,其余参数,会按顺序成为函数的入参,同时onClick这个回调携带的event信息,会作为最后一个入参传给函数