React 導讀(二)

前言

在上篇文章React 導讀(一)中學習到了寫第一個 Web 組件,這篇文章將繼續以前的目錄,開始新的知識點補充:javascript

  1. [x] React 如何編寫 Hello World!
  2. [x] React 中三個最基礎、最重要的東西
  3. [x] React 中的 JSX
  4. [x] 你的第一個 Web 組件
  5. [ ]React 中最開始須要關注的生命週期
  6. [ ]React 一個組件集合的簡單交互
  7. [ ]React 開始一個項目的一點建議
  8. [ ]React 簡單的項目結構組織

5、React 中最開始須要關注的生命週期

其實在學習 React 以前,就應該瞭解目前前端推薦的是組件化開發的方式,React 是讓組件化更加簡單的庫。那麼組件開發必不可少的就是生命週期,說直白一點就是運行組件的過程是 React 來作,運行過程當中須要有一些代碼鉤子來讓咱們去調用,在組件執行的某一個地方去執行咱們本身寫的代碼。這裏先介紹擁有的生命週期鉤子,下面的方法 constructorrender 不屬於生命週期,我按功能分類了一下,也就是學習的時候不必定要循序漸進,應該以學習以後能真正寫一些東西爲目標:前端

(1) 與組件掛載相關的方法,包括構造函數java

  • constructor
  • componentWillMount
  • componentDidMount
  • componentWillUnmount

最經常使用的生命週期應該是最後 2 個,constructorcomponentWillMount 目前先理解成能知足的功能大致相同,若是這裏解釋太複雜不太好。ajax

對於最開始關注的是:this.state 的初始化以及 ajax 在哪裏請求。typescript

this.state 在 constructor 進行初始化, ajax 推薦在 componentDidMount 中進行請求。
  • componentDidMount 就是在組件已經掛載到 DOM 中後的鉤子,能夠理解爲 jQuery 中提供的 ready 方法。
  • componentWillUnmount 是在組件即將被卸載前一刻的鉤子,通常用於取消 componentDidMount 中訂閱的事件等做用,清理一些不要的變量等,避免內存泄漏。

下面經過一個簡單的例子說明一下:編程

先有一個 foods.json 文件來模擬請求的後臺數據。json

[
    {
        "id": 1,
        "name": "香蕉"
    },
    {
        "id": 2,
        "name": "蘋果"
    },
    {
        "id": 3,
        "name": "獼猴桃"
    }
]
// 1. 掛載相關的生命週期
class CycleMount extends React.Component {
    constructor() {
        super();
        this.state = {
            foods: []
        };

        console.log('1. constructor 執行了...');
    }
    componentDidMount() {
        // 這裏使用原生的 fetch API 進行 ajax 請求,你也可使用 $.ajax 進行請求,原理是同樣的,重點是關注 setState 的地方
        fetch('/mock/foods.json',
            {
                method: 'GET',
                headers: new Headers({
                    'Accept': 'application/json'
                })
            }
        ).then(dataResult => {
            if(dataResult.status === 200) {
                return dataResult.json();
            } else {
                return [];
            }
        }).then(data => {
            // 這裏的 data 就是 foods.json 裏面的數據
            // 調用 setState 來更新 render 裏面調用的 this.state 的值
            this.setState({
                foods: data
            });
        });

        console.log('2. componentDidMount 執行了...');
    }
    render() {
        // foods 是一個數組,map 方法是數組自帶的方法,能夠查詢相關 api
        const foodItems = 
            this.state.foods.map(food => {
                return (<li key={food.id}>{food.name}</li>);
            });

        // 這裏是返回的最終組件結構
        return (
            <ul>
                {foodItems}
            </ul>
        );
    }
}

上面有了完整的註釋,也能看到基本上項目中可能會將代碼寫到何處,我也打了兩個日誌,來識別究竟是誰先執行,結果能夠本身運行一下,執行順序就是我標記的1,2。api

好了,基本的學習了,能夠本身動手試試訂閱一個事件,而後在卸載的時候取消這個事件。數組

(2) 優化相關app

  • shouldComponentUpdate

這個方法比較重要,可是我這裏不會介紹得太過於複雜,太複雜只會讓重要的部分不那麼突出。這個方法的返回值是一個 boolean 類型,分別代碼的意義:

  • true 組件應該更新,執行 render 方法以及相關生命週期;
  • false 組件狀態沒有更新,不執行 render 等方法,意味着網頁界面不會改變。

那麼它直觀上的做用是可以經過返回值來決定界面是否改變,實際的意義就是當咱們知道當前 oldState = this.state 的值和新的 newState = this.state 值徹底相等的時候(或者是新傳入的 props)就不用再浪費性能去從新渲染組件了。

API 上的定義是這樣的:

/* nextProps: 新的 props, nextState: 新的 state */
shouldComponentUpdate(nextProps, nextState): boolean

舉個例子來直觀說明一下:

class ComponentOptimize extends React.Component {
    state = {
        count: 0
    };
    shouldComponentUpdate(nextProps, nextState) {
        // 當 count > 10 的時候就不能再從新渲染組件了
        if(nextState.count > 10) {
            return false;
        }

        return true;
    }
    handleUpdateCount() {
        console.log('我點了一下哦!');
        this.setState(prevState => {
            return {
                count: prevState.count + 1
            };
        });
    }
    render() {
        return (
            <div onClick={this.handleUpdateCount.bind(this)} style={{cursor: 'pointer'}}>
                <h1>{this.state.count}</h1>
            </div>
        );
    }
}

圖片描述

你會發現 10 事後界面就沒有再更新過了,這樣應該很直觀了。

(3) Props 相關的生命週期

  • componentWillReceiveProps

這個主要是在組件的 props 傳入新的值後被調用,不論是不是傳的同樣的值或者 shouldComponentUpdate 返回了 false,看下例子吧:

class Cat extends React.Component {
    componentWillReceiveProps(nextProps) {
        console.log('改一次我執行一次!');
    }
    shouldComponentUpdate(nextProps, nextState) {
        // 改的名字同樣的時候
        return this.props.name !== nextProps.name;
    }
    render() {
        console.log('貓改了一次名字!');
        return (
            <h1>我有新名字了!{this.props.name}</h1>
        );
    }
}

class App extends React.Component {
    state = {
        catName: '嚕嚕'
    };
    handleChangeCatName() {
        const catNames = ['嚕嚕', '小白', '小黃', '小黑', '皮卡丘'];
        const catIndex = this.getSomeOneIndex();

        this.setState({
            catName: catNames[catIndex]
        });
    }
    getSomeOneIndex() {
        return Math.floor(Math.random() * 5 + 0);
    }
    render() {
        return (
            <div>
                {/* 給 Cat 傳新的名字 */}
                <Cat name={this.state.catName} />
                <button onClick={this.handleChangeCatName.bind(this)}>點我給貓咪取新名字!</button>
            </div>
        );
    }
}

最後確定每次點擊按鈕都會輸出這句的結果 console.log('改一次我執行一次!');

(4) 更新組件相關

  • componentWillUpdate
  • componentDidUpdate

由於都是講解 API,因此國際慣例的先看下 API 的定義吧:

// 組件更新前執行
componentWillUpdate(nextProps, nextState)
// 組件更新後執行
componentDidUpdate(prevProps, prevState)

能夠從定義中看出,它們都接受了兩個參數:props && state,不過看變量前綴可以聯想點什麼。

暫時想不到什麼實際項目的例子,隨便假設點內容吧。不過這裏須要注意的地方是:

  • 1. 這兩個方法裏面都不要調用 setState!
  • 2. 第一次初始化組件 render 的時候不會執行
  • 3. shouldComponentUpdate 返回 false 不會執行

第一條的緣由:容易造成一個遞歸的調用,不做就不會死...因此儘可能不要在這裏調~目前尚未碰到須要在這裏調的需求。
第二條的緣由:額,說好的更新才調,初始化不調用是符合邏輯的。
第三條的緣由:額,這 2 個鉤子是與組件更新相關的,因此也符合邏輯的,組件是否更新就是靠 shouldComponentUpdate 返回值。

在上面 Cat 的例子中加入下面的代碼能夠看下結果:

componentWillUpdate() {
    console.log('componentWillUpdate 執行了!')
}
componentDidUpdate() {
    console.log('componentDidUpdate 執行了!')
}

(5)組件錯誤

  • componentDidCatch

就是在組件發生異常的時候可能會被調用的鉤子,須要注意的有下面的地方:

  • 只能在父級組件捕獲子組件的異常;
  • 若是異常被 try...catch 包裹父級組件的鉤子就不會執行了。

看個例子吧:

class Cat extends React.Component {
    componentWillReceiveProps(nextProps) {
        // 這裏手動拋一個異常,觸發咱們的鉤子 componentDidCatch
        throw new Error('miao miao~');
    }
    render() {
        let miao = this.props.name;

        return (
            <div>
                {miao}
            </div>
        );
    }
}

class App extends React.Component {
    state = {
        catName: '嚕嚕',
        isError: false,
    };
    handleChangeCatName() {
        const catNames = ['嚕嚕', '小白', '小黃', '小黑', '皮卡丘'];
        const catIndex = this.getSomeOneIndex();

        this.setState({
            catName: catNames[catIndex]
        });
    }
    getSomeOneIndex() {
        return Math.floor(Math.random() * 5 + 0);
    }
    componentDidCatch(error, info) {
        console.log(error, info);
        if(error) {
            // 若是有錯誤信息,就從新渲染一下組件,多是更好的交互
            this.setState({
                isError: true
            });
        }
    }
    render() {
        return (
            <div>
                <Cat name={this.state.catName} />
                {!this.state.isError ?
                    <button onClick={this.handleChangeCatName.bind(this)}>點我給貓咪取新名字!</button> :
                    <p>不要奴才給我取名字了!</p>
                }
            </div>
        );
    }
}

(6) 渲染相關

  • render

額...這個不想寫了,先睡覺吧~應該寫了這麼多個小例子也差很少了~能夠動手試試哦!還不清楚的能夠 Google 一下,你就知道。

生命週期很重要,其實學到這裏也差很少能夠上手寫點項目熟練一下了,其餘的更可能是思惟和編程方面的東西,週期的篇幅單獨來一篇吧~其餘主題以後再繼續吧!
相關文章
相關標籤/搜索