if you’re curious… it’s nice to know how it works.
函数组件和类组件有什么区别 从一个 demo (父组件传入 select 中的 value,子组件设置一个 3s 的延时打印这个 value,在 3s 中我们改变 select 中的 value 看看子组件最后打印了啥) 来看区别
两个组件的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 export default class ClassButton extends React .Component { showMessage = () => { alert('Followed ' + this .props.user); }; handleClick = () => { setTimeout (this .showMessage, 3000 ); }; render ( ) { return <button onClick ={this.handleClick} > Follow(class)</button > ; } }
1 2 3 4 5 6 7 8 9 10 11 function FunctionButton (props ) { const showMessage = () => { alert('Followed ' + props.user); }; const handleClick = () => { setTimeout (showMessage, 3000 ); }; return <button onClick ={handleClick} > Follow(Function)</button > ; }
父组件代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class App extends React .Component { state = { user : `` }; changeSelect = ({ target: { value } } ) => { this .setState({ user : value }); }; render ( ) { return ( <div className ='App' > <select onChange ={this.changeSelect} > <option value ='' > Choose a name</option > <option value ='tiny' > tiny</option > <option value ='mini' > mini</option > </select > <ClassButton user ={this.state.user} /> <FunctionButton user ={this.state.user} /> </div > ); } }
最终结果 :class 组件打印的是改变后的值,function 组件打印的是改变前的值。
说到改变 就容易让人产生迷惑,props 不是不可变吗?
props 是不可变这点没错,但是 this
总是可变的,React 每次 re-render 都会让 this
变动,所以 class 组件中拿到的 user
永远都是最新的 props
。
但是如果想拿到当前 的 props
应该怎么办呢,答案也很简单:在 render
函数中加上 const props = this.props
,但是这样还是存在一个小问题 的,showMessage
函数如果想拿到 props
中的 user
岂不是也要把函数写在 render
函数中吗,这样 class 的方法似乎就没有什么意义了。
再来看下 Function 组件带来的结果,是符合我们需求的,暂定这种特性叫做 Capture Value 吧。
Function components capture the rendered values.
函数式组件中没有用到 this
,而是传递的 props
,那结果想必也一清二楚了。
在 Hooks 中也会有这样的特性, 这边就不贴代码了,Hooks 只是写法上有些不同。
但是如果想要 Function 组件规避 Capture Value 属性需要怎么做?
可以利用 useRef
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 export default function HooksButton ( ) { const latestMessage = useRef('' ); const [message, setMessage] = useState('' ); const showMessage = () => { alert('You said: ' + latestMessage.current); }; const handleSendClick = () => { setTimeout (showMessage, 3000 ); }; const handleMessageChange = e => { setMessage(e.target.value); latestMessage.current = e.target.value; }; return ( <> <input value ={message} onChange ={handleMessageChange} /> <button onClick ={handleSendClick} > Send</button > </> ); }
如何区分开 function 和 class 组件 在 JavaScript 中,「如何区分 function 和 class」就是一个经典问题。
在 StackOverflow 上可以搜到区分 JS 中的 function
和 class
的相关方法,其中最高票回答是
1 2 3 4 5 6 7 8 9 10 11 12 function isClass (func ) { return ( typeof func === 'function' && /^class\s/ .test(Function .prototype.toString.call(func)) ); } class A {}function B ( ) {}Function .prototype.toString.call(A); Function .prototype.toString.call(B);
原因也很简单,因为在 ES6 的规范中 , toString
也支持了 ClassDeclaration。
不过在 Babel 中,class
是转译成 function
的,也就是说 Babel
环境下, class
和 function
只是名字不同的 function
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class A {}('use strict' ); function _instanceof (left, right ) { if ( right != null && typeof Symbol !== 'undefined' && right[Symbol .hasInstance] ) { return !!right[Symbol .hasInstance](left); } else { return left instanceof right; } } function _classCallCheck (instance, Constructor ) { if (!_instanceof(instance, Constructor)) { throw new TypeError ('Cannot call a class as a function' ); } } var A = function A ( ) { _classCallCheck(this , A); };
React 中是如何检查 class 组件的 1 2 3 4 5 class Greeting extends React .Component { render ( ) { return <p > Hello</p > ; } }
想必大家对 JS 中的原型链有所认识。
x instanceof Y
做的事就是沿着 x.__proto__
寻找 Y.prototype
这也可以用来检查一个类是否拓展了另一个类。
1 Greeting.prototype instanceof React.Component;
不过这样在多副本的 React 中会失效
基于上面的一些原因,React 采用的是下面的方式 来检查 class:
React.Component.prototype 上会挂着 isReactComponent
1 2 class Greeting extends Component {}console .log(Greeting.prototype.isReactComponent);
参考: