React 16

MIT License

採用新核心 Fiber

  • 提升渲染效率: Split rendering work into chunks and spread it out over multiple frames.
  • 支援新的 render() return type
  • 支援新的 portal 渲染方式

React.Component 新特徵

Portal
  • FEATURE:
    1. 在元件中, 可以指定渲染 child 在元件外的 DOM 上
    2. 雖然實際的 DOM Tree 上, Portal 存在於元件外, 但在 React DOM Tree 上, 仍視為存在於元件內, 因此仍會觸發 Event Bubbling.
  • USAGE: 呼叫 React.createPortal(child, container)
  • DEMO:
class Message extends React.Component {
	/**
	 * @param {{message:string, modalMessage:string}} props
	 */
	constructor(props) {
		super(props);
		this.state = {showModal: false};
	}
	render() {
		let modal = null;
		if (this.state.showModal) {
			let body = document.body;
			modal = React.createPortal(
				<div className="modal">
					{this.props.modalMessage}
				</div>,
				body
			)
		}
		return (
			<div>
				<span>this.props.message</span>
				<button
					onClick={() => this.setState({showModal: true})}
				>
					show modal
				</button>
				{modal}
			</div>
		);
	}
}
render()
  • FEATURE:
    • 能夠接受 Array Type 作為 return
    • 能夠接受 Portal 作為 return
    • 能夠接受 stringnumber 作為 return
    • 能夠接受 booleannull 作為 return
      • 若 return falsenull, ReactDOM.findDOMNode(this) return null
Props and DOM Attributes in JSX.
  • FEATURE:
    1. 自定義 DOM Attribute 名稱不再局限於 data-aria-, 所有未被辨識為 元件 props 的屬性, 都將被視為 DOM Attributes,但仍建議以 aria- 為關鍵字
    2. 如賦予的值不符合 DOM Attributes 的需求, 將被轉型為字串, 且於 console 提示錯誤:
//Before
<div customattribute={new Object()} ></div>
<div customattribute={42}></div>

//After
<div customattribute="[Object object]">
<div customattribute="42"></div>
  • DEMO:
class Button extends React.Component {
	/**
	 * @param {{label:string, onClick:Function}} props
	 */
	constructor(props) {
		super(props);
		this.onClick = this.props.onClick.bind(this);
	}
	render() {
		return (
			<button onClick={this.onClick}>
				{this.props.label}
			</button>
		);
	}
}

class Container extends React.Component {
	constructor(props) {
		super(props);
	}
	render() {
		let click = () => alert("Clicked");
		return(
			<div>
				<Button label="MyButton" click={click}/>
			</div>
		);
	}
}

//實際渲染出來的 DOM
/*
<div>
	<button click="[Function click]">
		MyButton
	</button>
</div>
*/

元件 Update 規則調整

setState()
  • FEATURE:
    1. setState(null) 將不再觸發 update
    2. render() 中呼叫 setUpdate, 必定觸發 update
    3. setStatecallback function 將在 componentDidMount()componentDidUpdate() 後馬上觸發
componentWillMount() 及 componentWillUnmount()
  • FEATURE: 若同一元件內的子元件替換, 子元件的 componentWillMount() 必定先於 componentWillUnmount() 執行
  • DEMO:
class ChildA extends React.Component {
	constructor(props) {super(props);}
	componentWillMount() {console.log("Child A will be mounted")}
	componentWillUnmount() {console.log("Child A will be unmounted")}
	render() {return <div>Child A</div>}
}

class ChildB extends React.Component {
	constructor(props) {super(props);}
	componentWillMount() {console.log("Child B will be mounted")}
	componentWillUnmount() {console.log("Child B will be unmounted")}
	render() {return <div>Child B</div>}
}

class Container extends React.Component {
	constructor(props) {
		super(props);
		this.state = {childType: false}
	}
	changeChild() {
		this.setState((prevState) => {
			return {childType: !prevState.childType}
		})
	}
	render() {
		let child = this.state.childType ? <ChildA/> : <ChildB/>
		return (<div>{child}</div>);
	}
}
ReactDOM.render()
  • FEATURE: 如果在元件內的生命週期函數呼叫 ReactDOM.render(), 則 ReactDOM.render() return null

Error Handling

如果在元件生命週期中發生錯誤, 整個元件將不被 render, 以避免呈現有問題的資訊給使用者; 或是使用 Error Boundary 處理錯誤, 提供 error 相應的 UI.

Error Boundary
  • FEATURE:
    1. 使用 React.Component 提供的 API, 處理 error; 並提供開發者因應 error 提供不同 UI 的時機.
  • USAGE:
    1. 於元件中 override componentDidCatch(error, info) API
    2. 僅能處理元件中子元件發生的 error
  • DEMO:
class Child extends React.Component {
	constructor(props) {
		super(props);
	}
	render() {
		return(<button onClick={this.props.action}>Button</button>)
	}
}

class ErrorBoundary extends React.Component {
	constructor(props) {
		super(props);
	}
	componentDidCatch(error, info) {
		console.log(error);
	}
	render() {
		return (
			<div>
				//此處的 this.props.action 若沒定義, 若點擊, 不會觸發 componentDidCatch
				<button onClick={this.props.action}>Error Button</button>
				//此處因沒賦予 action 參數, 若點擊, 會觸發componentDidCatch
				<Child/>
			</div>
		);
	}
}

應用於項目中

SPEC
  1. 由於 React 16 使用了 ES6 的 MapSet Type, 因此需要以 JavaScript Library 補齊此 Type. 官方文檔建議使用以下兩種解決方案:
    1. core-js
    2. babel-polyfill
  2. 仰賴 requestAnimationFrame
棄用 react-addons
  • 個別 Addon 需要個別引用 Library
  • 目前項目受影響的部分為 CSSTransitionGroup
Library 修改
BeforeAfter
react/dist/react.jsreact/umd/react.development.js
react/dist/react.min.jsreact/umd/react.production.min.js
react-dom/dist/react-dom.jsreact-dom/umd/react-dom.development.js
react-dom/dist/react-dom.min.jsreact-dom/umd/react-dom.production.min.js