函数组件与类组件区别

1,345 阅读4分钟

推荐阅读原文,别听我逼逼。还记得那场马拉松吗?领头的人跑错路了,结果你知道的。原文链接:

How Are Function Components Different from Classes?

先看一下,两段不一样的代码:(抱歉,开场就放大招是我的不对,但将错就错)

Function的:

function CounterFunction(props) {
    const { count } = props;
    const showAlert = () => {
        setTimeout(() => {
            alert(count + '--from Function');
        }, 3000);
    };
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={showAlert}>Show count</button>
        </div>
    );
}

Class的:

class CounterClass extends React.Component {
    showAlert = () => {
        setTimeout(() => {
            alert(this.props.count + '--from Class');
        }, 3000);
    };

    render() {
        return (
            <div>
                <p>He clicked {this.props.count} times</p>
                <button onClick={this.showAlert.bind(this)}>
                  Show count
                </button>
            </div>
        );
    }
}

调用:

class App extends React.Component {
    state = {
        count : 123
    };
    render() {
        return (
            <div className="App" onClick={() => this.setState({count: 456})}>
                <CounterFunction count={this.state.count}/>
                <CounterClass count={this.state.count} />
            </div>
        );
    }
}

当你两次调用的时候,你就会发现他们的页面渲染是相同的,count值都会变成456,但是,alert出来的值可就不一样了。

Function的是123,Class的是456。所以当showAlert事件发生的时候,Function取的是发生前的值,是触发事件前的值,就像处对象,哪怕分手后,双方立刻找到了新对象,但仍然记得当初的美好。

Class取的是最新的值,可谓是喜新厌旧的典范了,有了新的TA,立刻忘记旧的TA,记录的是最新的美好。

全文中心的体现,以免被看官老爷们误以为作者在写离题作文:

函数组件获取的总是事件发生的时候的值,即使值在事件发生后已经更新了。

而,类组件获取的总是最新的值。

其实,千人读《哈姆雷特》就有千种哈味,所以特意贴出原文中的原句给大家,希望大家有自己的理解。

Function components capture the rendered values --How Are Function Components Different from Classes?

当然,如果函数组件想要重新处对象,记录最新的美好,也是可以的。借用ref。

// 假如使用react hooks
const ref = useRef(null);
// 可以使用ref.current指向count(你想要获取的值),那样你获取的值都是最新的值了。

下面来探究,为什么函数组件与类组件获取到的值会有差异。如果你的英语技能已经点满或者比较高级(阅读原文获得更大的快乐),你可能会嫌弃我的解答,因为我,划船全靠浪,翻译全靠猜...

首先,探究类组件获取值的过程:

从上面的Class的代码,我们很明显地看到 count 源自 this.props 。一般来说,props 是稳定不变的,那么获取到的值应该也是稳定的才对,但是 this 会变化。当组件接受父组件传入的 count 发生变化的时候,组件将会会重渲染(re-render),以更新显示变化了的 count ,所以 this 被刷新了,可见,组件已经被人釜底抽薪了,从头到尾都是全新的一个娃了。

所以,this.props.count 理所当然是最新的值的啊。

贴上原文给各位看官老爷们练练技能,如果我理解错了,请指正!

This class method reads from this.props.user. Props are immutable in React so they can never change. However, this is, and has always been, mutable.

Indeed, that’s the whole purpose of this in a class. React itself mutates it over time so that you can read the fresh version in the render and lifecycle methods.

贡献个方法解决这种喜新厌旧的家伙:

基础版:先将可能会变换的值this.props.count)(假如叫奥巴马)保存下来(假如保存的变量叫川普),然后采用参数传入的方式,将变量(川普)传进去。那么很明显,这个变量的值(川普)是确定的,即使值(奥巴马)发生了变化,(川普)也不会变化。

但是缺陷也很明显,如果还是用了其他的变量,也需要经历这样的处理,显然,这不是很适合。当然,针对少量的,这种方法还是很方便的。

class CounterClass extends React.Component {
    showAlert = count => {
        alert(count + '--from Class');
    };
    clickEvent = () => {
        const {count} = this.props;
        setTimeout(() => this.showAlert(count), 3000);
    };
    render() {
        return (
            <div>
                <p>He clicked {JSON.stringify(this.props.count)} times</p>
                <button onClick={this.clickEvent}>
                    Show count
                </button>
            </div>
        );
}

**进阶版:**在render方法内,保存 this.props 为变量。

class CounterClass extends React.Component {
    render() {
      const {count} = this.props;
      showAlert = () => {
        setTimeout(() => {
            alert(count + '--from Class');
        }, 3000);
    	};
      return (
        <div>
        	<p>He clicked {count} times</p>
      		<button onClick={showAlert}>
        		Show count
         	</button>
     		</div>
      );
    }
}

现在,请将目光移到函数组件内

观察一下,函数组件内的 count 来自于哪里。

function CounterFunction(props) {
    const { count } = props;
  	...
}

来源于传入的参数 props ,如果采用解构手法,你会更清晰地感知:

function CounterFunction({ count }) {
  	...
}

请参考一下上面收拾类组件喜新厌旧的方法,或许会有一点小提示哦。

类组件拿到事件发生前的值,采用的方法步骤是:1. 将可能变化的值保存为变量,2. 将这个变量传入方法内。

那么,函数组件的值,本来就是传参进来的,那么相当于已经走到了第2步

所以现在,你知道函数组件获取的总是事件发生的时候的值,即使值在事件发生后已经更新了 的原因了吧。

请轻喷,呵护作者!