從React 16.0開始,React引入了一下新的特性(組件、API、生命週期函數)。從15.X升級到16.X是一種加強式的升級即以前的用法在新版上保持有效,基本是平滑的升級,可是隨着React 16.5的發佈,新特性基本已經完善和固定下來,爲了享受新版本帶來的更高效率架構和更規範的開發流程,如今是時候使用上React ^16.0 的新特性進行代碼重構。react
我直接使用了React 16.5,具體請查看React 更新日誌如下是幾個經常使用的新特性,每一個特性都從與以前的比較和實例來講明:git
生命週期函數的改動有兩個方面github
static getDerivedStateFromProps(nextProps, prevState) getSnapshotBeforeUpdate(prevProps, prevState) componentDidCatch(error, info)安全
UNSAFE_componentWillMount(nextProps, nextState) UNSAFE_componentWillReceiveProps(nextProps) UNSAFE_componentWillUpdate(nextProps, nextState)bash
在16.X版本中依然可使用不安全的周期函數(UNSAFE_前綴可加可不加),用法也和以前的同樣。可是若是使用了新增的周期函數就不能同時使用不安全的周期函數,不然會報錯而且不安全的周期函數不會被執行。架構
使用新增的周期函數的執行順序仍是按照階段來講明:dom
constructor -> getDerivedStateFromProps -> render -> componentDidMountide
getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate函數
componenWillUnMountui
getDerivedStateFromProps周期函數的做用是根據props初始化或更新state,有兩個參數分別是nextProps(本次渲染的props)和prevState(當前組件的state),判斷nextProps和prevState對應的屬性是否相同,返回須要更改的state。
React 15.X 初始化和更新分別寫在constructor和componentWillReceiveProps.
class Parent extends Component{
render(){
return (
<Child age={18} />
)
}
}
class Child extends Components{
constructor(props){
super(props);
this.state = {
age: this.props.age //裝載階段初始化state
}
}
componentWillReceiveProps(nextProps){
if(nextProps.age !== this.state.age){
this.setState({
age: nextProps.age
}) // 修改state
}
}
render(){...}
}
React 16.5 寫在getDerivedStateFromProps便可
class Child extends Component{
constructor(props){
super(props);
this.state = {
age: ''
}
}
static getDerivedStateFromProps(nextProps,prevState){
if(nextProps.age !== prevState.age){
return {
age: nextProps.age
}
}
return null;
}
render(){...}
}
複製代碼
getSnapshotBeforeUpdate做用是替代componentWillUpdate,有兩個參數分別是prevProps(未更新前的props)和prevState(未更新前的state)。最大的區別是它放在了render函數後執行,爲未來的Fiber架構作準備;還有的區別是componetWillUpdate的參數是nextProps(本次更新的props)和nextState(本次更新的state),在轉換時須要注意。
列表項數目改變時scroll到最後一條
getSnapshotBeforeUpdate(prevProps, prevState) {
if (prevProps.list.length !== this.props.list.length) {
const list = this.listRef.current //dom渲染前的列表元素ref
return list.scrollHeight - list.scrollTop
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot) {
// snapshot 是getSnapshotBeforeUpdate返回的偏移量
if (snapshot !== null) {
const list = this.listRef.current //dom渲染後的列表元素ref
list.scrollTop = list.scrollHeight - snapshot
}
}
複製代碼
componentDidCatch React組件發生錯誤時的處理邏輯,調用第三方的錯誤監聽服務。
使用Context 主要是解決props向多層嵌套的子組件傳遞的問題,原理是定義了一個全局對象。新的Context的使用方法跟舊的有很大差異
// React 15.x須要本身開發定義提供 Context的父組件,而後用這個父組件包裹嵌套多層的子組件。
class Provider extends Component {
getChildContext(){
return {
age: 18
}
}
render() {
return this.props.children;
}
}
Provider.childContextTypes = {
age: PropTypes.number
};
// 子組件
class Child extends Component {
constructor(props,context) {
super(props,context); //super 帶上context 參數
}
render(){
const {age} = this.context; //使用this.context訪問
...
}
}
Child.contextTypes = {
age: PropTypes.number
};
// React 16.5 使用 React.createContext()
const MyContext = React.createContext();
// 使用 MyContext.Provider 包裹子組件
<MyContext.Provider value={{age: 18}}>
<Child />
</MyContext.Provider>
//子組件使用 MyContext.Consumer得到 Context
class Child extends Component {
render(){
return (
<MyContext.Consumer>
{(context) => {
const {age} = context;
return ...
}}
</MyContext.Consumer>
)
}
}
複製代碼
Portal 是React 16 的全新特性,能夠將組件渲染到非根節點其餘 DOM 節點上,由於以前版本沒有這個功能,因此使用場景須要逐步發掘。用法是使用ReactDOM.createPortal(component, domNode) component是要渲染的組件,domNode是掛載的dom節點。
React 16 使用React.createRef 建立Ref對象,如下是使用的區別:
// React 15.X
class Parent extends Component {
getChildRef(ref){
this.myRef = ref;
}
render() {
<Child childRef={this.getChildRef} />
}
}
class Child extends Component {
constructor(props) {
super(props)
}
render() {
return <div ref={this.props.childRef} />
}
}
// React 16.5
class Parent extends Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return <Child ref={this.myRef} />
}
}
const Child = React.forwardRef((props, ref) => <div ref={ref} />);
複製代碼