組件的各種概念解析
阿新 • • 發佈:2019-03-11
另一個 element 屬性 extend clas cti instance mod 靜態方法
1.類組件
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
// 註意: props只讀
2.函數組件
function Welcome(props) { // 組件名首字母必須大些 return <h1>Hello, {props.name}</h1>; // Props只讀 } const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById(‘root‘) ); // 1.我們對<Welcome name="Sara" />元素調用了ReactDOM.render()方法。 // 2.React將{name: ‘Sara‘}作為props傳入並調用Welcome組件。 // 3.Welcome組件將<h1>Hello, Sara</h1>元素作為結果返回。 // 4.React DOM將DOM更新為<h1>Hello, Sara</h1>。
函數組件和類組件是將props屬性轉變成UI
高階組件則是將一個組件轉換成另一個組件
3.高階組件
高階組件就是一個函數,且該函數接受一個組件作為參數,並返回一個新的組件。
高階組件就是一個沒有副作用的純函數。
不要改變原始組件,使用組合
例如:Redux的connect
方法
// This function takes a component... function withSubscription(WrappedComponent, selectData) { // ...and returns another component... return class extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = { data: selectData(DataSource, props) }; } componentDidMount() { // ... that takes care of the subscription... DataSource.addChangeListener(this.handleChange); } componentWillUnmount() { DataSource.removeChangeListener(this.handleChange); } handleChange() { this.setState({ data: selectData(DataSource, this.props) }); } render() { // ... and renders the wrapped component with the fresh data! // Notice that we pass through any additional props return <WrappedComponent data={this.state.data} {...this.props} />; } }; }
約定:貫穿傳遞不相關props屬性給被包裹的組件
render() { // 過濾掉專用於這個階組件的props屬性, // 不應該被貫穿傳遞 const { extraProp, ...passThroughProps } = this.props; // 向被包裹的組件註入props屬性,這些一般都是狀態值或 // 實例方法 const injectedProp = someStateOrInstanceMethod; // 向被包裹的組件傳遞props屬性 return ( <WrappedComponent injectedProp={injectedProp} {...passThroughProps} /> ); }
約定:最大化的組合性
// 不要這樣做…… const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent)) // ……你可以使用一個函數組合工具 // compose(f, g, h) 和 (...args) => f(g(h(...args)))是一樣的 const enhance = compose( // 這些都是單獨一個參數的高階組件 withRouter, connect(commentSelector) ) const EnhancedComponent = enhance(WrappedComponent)
單獨一個參數的高階組件,類似 connect
函數返回的,簽名是Component => Component
。輸入和輸出類型相同的函數確實是很容易組合在一起。
約定:包裝顯示名字以便於調試
function withSubscription(WrappedComponent) { class WithSubscription extends React.Component {/* ... */} WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`; return WithSubscription; } function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || ‘Component‘; }
React Developer Tools
便於調試,
註意:
不要在render方法內使用高階組件
原因: (差分算法,渲染性能問題)
render() { // 每一次渲染,都會創建一個新的EnhancedComponent版本 // EnhancedComponent1 !== EnhancedComponent2 const EnhancedComponent = enhance(MyComponent); // 那引起每一次都會使子對象樹完全被卸載/重新加載 return <EnhancedComponent />; }
必須將靜態方法做拷貝
1)可借助hoist-non-react-statics
function enhance(WrappedComponent) { class Enhance extends React.Component {/*...*/} // 必須得知道要拷貝的方法 :( Enhance.staticMethod = WrappedComponent.staticMethod; return Enhance; }
2)分別導出組件自身的靜態方法。
// Instead of... MyComponent.someFunction = someFunction; export default MyComponent; // ...export the method separately... export { someFunction }; // ...and in the consuming module, import both import MyComponent, { someFunction } from ‘./MyComponent.js‘;
Refs屬性不能貫穿傳遞
如果你向一個由高階組件創建的組件的元素添加ref應用,那麽ref指向的是最外層容器組件實例的,而不是被包裹的組件。
現在我們提供一個名為 React.forwardRef
的 API 來解決這一問題(在 React 16.3 版本中)
組件的各種概念解析