Appearance
装饰器
本节目标:理解“装饰器”的核心思路,并能把它用于实际开发或面试表达。
cra支持装饰器
安装npm i react-app-rewired customize-cra @babel/plugin-proposal-decorators -D
修改
package.json
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" }config-overrides.js
const {override,addBabelPlugin} = require('customize-cra');
module.exports = override( addBabelPlugin( [ "@babel/plugin-proposal-decorators", { "legacy": true } ]))
jsconfig.json
{ "compilerOptions": { "experimentalDecorators": true }}属性代理
- 基于属性代理:操作组件的
props
import React from 'react';import ReactDOM from 'react-dom';const loading = message =>OldComponent =>{ return class extends React.Component{ render(){ const state = { show:()=>{ let div = document.createElement('div'); div.innerHTML = `<p id="loading" style="position:absolute;top:100px;z-index:10;background-color:black">${message}</p>`; document.body.appendChild(div); }, hide:()=>{ document.getElementById('loading').remove(); } } return ( <OldComponent {...this.props} {...state} {...{...this.props,...state}}/> ) } }}@loading('正在加载中
')class Hello extends React.Component{ render(){ return <div>hello<button onClick={this.props.show}>show</button><button onClick={this.props.hide}>hide</button></div>; }}let LoadingHello = loading('正在加载
')(Hello);
ReactDOM.render( <LoadingHello/>, document.getElementById('root'));反向继承
- 基于反向继承:拦截生命周期、
state、渲染过程
import React from 'react';import ReactDOM from 'react-dom';class Button extends React.Component{ state = {name:'张三
'} componentWillMount(){ console.log('Button componentWillMount'); } componentDidMount(){ console.log('Button componentDidMount'); } render(){ console.log('Button render'); return <button name={this.state.name} title={this.props.title}/> }}const wrapper = OldComponent =>{ return class NewComponent extends OldComponent{ state = {number:0} componentWillMount(){ console.log('WrapperButton componentWillMount'); super.componentWillMount(); } componentDidMount(){ console.log('WrapperButton componentDidMount'); super.componentDidMount(); } handleClick = ()=>{ this.setState({number:this.state.number+1}); } render(){ console.log('WrapperButton render'); let renderElement = super.render(); let newProps = { ...renderElement.props, ...this.state, onClick:this.handleClick } return React.cloneElement( renderElement, newProps, this.state.number ); } }}let WrappedButton = wrapper(Button);ReactDOM.render( <WrappedButton title="标题
"/>, document.getElementById('root'));
src\react.js
import { wrapToVdom } from "./utils";import { Component } from './Component';import { REACT_FORWARD_REF_TYPE, REACT_CONTEXT, REACT_PROVIDER } from './constants';function createElement(type, config, children) { let ref; let key; if (config) { delete config.__source; delete config.__self; ref = config.ref; delete config.ref; key = config.key; delete config.key; } let props = { ...config }; if (arguments.length > 3) { props.children = Array.prototype.slice.call(arguments, 2).map(wrapToVdom); } else { props.children = wrapToVdom(children); } return { type, ref, key, props, };}function createRef() { return { current: null };}function forwardRef(render) { var elementType = { $$typeof: REACT_FORWARD_REF_TYPE, render: render }; return elementType;}function Fragment(props) { return props.children;}function createContext() { let context = { $$typeof: REACT_CONTEXT }; context.Provider = { $$typeof: REACT_PROVIDER, _context: context } context.Consumer = { $$typeof: REACT_CONTEXT, _context: context } return context;}+function cloneElement(element, newProps, ...newChildren) {+ let oldChildren = element.props && element.props.children;+ let children = [...(Array.isArray(oldChildren) ? oldChildren : [oldChildren]), ...newChildren]+ .filter(item => item !== undefined)+ .map(wrapToVdom);+ if (children.length === 1) children = children[0];+ let props = { ...element.props, ...newProps, children };+ return { ...element, props };+}const React = { createElement, Component, createRef, forwardRef, Fragment, createContext,+ cloneElement};export default React;