React基礎(chǔ)知識點總結(jié)

本文目錄:

  • 1.React入口
  • 2.JSX語法
  • 3.組件
  • 4.正確使用setState
  • 5.生命周期
  • 6.組件復(fù)合
  • 7.redux
  • 8.react-redux
  • 9.react-router
  • 10.PureComponent
  • 11.認(rèn)識Hook
  • 12.自定義Hook與Hook使用規(guī)則
  • 13.Hook API之useMemo與useCallback

參考網(wǎng)址:https://zh-hans.reactjs.org/docs/getting-started.html

1.React入口

起步

  1. 創(chuàng)建項目: npx create-react-app my-app
  2. 打開項目: cd my-app
  3. 啟動項目: npm start
  4. 暴露配置項: npm run eject
    注意:暴露配置項操作是不可逆的

cra文件結(jié)構(gòu)

├── README.md 文檔
├── public 靜態(tài)資源
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src 源碼
  ├── App.css
  ├── App.js 根組件
  ├── App.test.js
  ├── index.css 全局樣式
  ├── index.js 入口文文件
  ├── logo.svg
  └── serviceWorker.js pwa支持
├── package.json npm 依賴

入口文件定義置媳,webpack.config.js

entry: [
// WebpackDevServer客戶端肄鸽,它實現(xiàn)開發(fā)時熱更新功能
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
// 應(yīng)用程序入口:src/index
paths.appIndexJs,
].filter(Boolean),

webpack.config.js 是webpack配置?文件彻舰,開頭的常量聲明可以看出cra能夠支持ts、sass及css模塊化。

// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;

React和ReactDom

刪除src下面所有代碼芍碧,新建index.js飞醉。

import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<h1>Hello React</h1>, document.querySelector('#root'));

React負(fù)責(zé)邏輯控制芍躏,數(shù)據(jù) -> VDOM
ReactDom渲染實際DOM奠涌,VDOM -> DOM

React使用JSX來描述UI
babel-loader把JSX 編譯成相應(yīng)的 JS 對象宪巨,React.createElement再把這個JS對象構(gòu)造成React需要的虛擬dom。

2.JSX語法

JSX是一種JavaScript的語法擴展溜畅,其格式?比較像模版語言捏卓,但事實上完全是在JavaScript內(nèi)部實現(xiàn)的。
JSX可以很好地描述UI慈格,能夠有效提高開發(fā)效率怠晴,

2.1.基本使用

表達(dá)式{}的使用,index.js

const name = "react study";
const jsx = <div>hello, {name}</div>;

2.2.函數(shù)

函數(shù)也是合法表達(dá)式浴捆,index.js

const obj = {
fistName: "Harry",
lastName: "Potter"
};
function formatName(name) {
return name.fistName + " " + name.lastName;
}
const jsx = <div>{formatName(user)}</div>;

2.3.對象

jsx是js對象蒜田,也是合法表達(dá)式,index.js

const greet = <div>good</div>;
const jsx = <div>{greet}</div>;

2.4.條件語句

條件語句可以基于上?結(jié)論實現(xiàn)选泻,index.js

const show = true;//false;
const greet = <div>good</div>;
const jsx = (
<div>
  {/* 條件語句 */}
  {show ? greet : "登錄"}
  {show && greet}
</div>
);

2.5.數(shù)組

數(shù)組會被作為一組子元素對待冲粤,數(shù)組中存放一組jsx可用于顯示列表數(shù)據(jù)

const a = [0, 1, 2];
const jsx = (
<div>
  {/* 數(shù)組 */}
  <ul>
    {/* diff時候,?首先?比較type页眯,然后是key梯捕,所以同級同類型元素,key值必須得 唯一 */}
    {a.map(item => (
        <li key={item}>{item}</li>
    ))}
  </ul>
</div>
);

2.6.屬性的使用

import logo from "./logo.svg";
const jsx = (
<div>
{/* 屬性:靜態(tài)值用雙引號餐茵,動態(tài)值用花括號科阎;class、for等要特殊處理忿族。 */}
<img src={logo} style={{ width: 100 }} className="img" />
</div>
);

2.7.模塊化

css模塊化锣笨,創(chuàng)建index.module.css,index.js

import style from "./index.module.css";
<img className={style.logo} />

或者npm install sass -D

import style from "./index.module.scss";
<img className={style.logo} />

更多css modules規(guī)則參考
http://www.ruanyifeng.com/blog/2016/06/css_modules.html

3.組件

組件道批,從概念上類似于 JavaScript 函數(shù)错英。它接受任意的入?yún)ⅲ?“props”),并返回用于描述頁面展示內(nèi)容的 React 元素隆豹。
組件有兩種形式:class組件和function組件椭岩。

3.1.class組件

class組件通常擁有狀態(tài)和生命周期,繼承于Component璃赡,實現(xiàn)render方法判哥。用class組件創(chuàng)建?個Clock

import React, {
    Component
} from "react";
export default class ClassComponent extends React.Component {
    constructor(props) {
        super(props);
        // 使用state屬性維護(hù)狀態(tài),在構(gòu)造函數(shù)中初始化狀態(tài)
        this.state = {
            date: new Date()
        };
    }
    componentDidMount() {
        // 組件掛載之后啟動定時?每秒更新狀態(tài)
        this.timerID = setInterval(() => {
            // 使用setState?方法更新狀態(tài)
            this.setState({
                date: new Date()
            });
        }, 1000);
    }
    componentWillUnmount() {
        // 組件卸載前停止定時?
        clearInterval(this.timerID);
    }
    componentDidUpdate() {
        console.log("componentDidUpdate");
    }
    render() {
        return <div > {
            this.state.date.toLocaleTimeString()
        } < /div>;
    }
}

3.2.function組件

函數(shù)組件通常無狀態(tài)碉考,僅關(guān)注內(nèi)容展示塌计,返回渲染結(jié)果即可

從React16.8開始引入了hooks,函數(shù)組件也能夠擁有狀態(tài)侯谁。

用function組件創(chuàng)建一個Clock:

import React, { useState, useEffect } from "react";
export function FunctionComponent(props) {
const [date, setDate] = useState(new Date());
useEffect(() => {//副作用
  const timer = setInterval(() => {
    setDate(new Date());
  }, 1000);
  return () => clearInterval(timer);//組件卸載的時候執(zhí)行
}, []);
return (
  <div>
    <h3>FunctionComponent</h3>
    <p>{date.toLocaleTimeString()}</p>
  </div>
);
}

提示: 如果你熟悉 React class 的生命周期函數(shù)锌仅,你可以把 useEffect Hook 看做componentDidMount 章钾, componentDidUpdate 和componentWillUnmount 這三個函數(shù)的組合。

4.正確使用setState

setState(partialState, callback)

  1. partialState : object|function
    用于產(chǎn)生與當(dāng)前state合并的子集热芹。
  2. callback : function
    state更新之后被調(diào)用贱傀。

4.1.關(guān)于 setState() 你應(yīng)該了了解三件事:

不要直接修改 State
例如,此代碼不會重新渲染組件:

// 錯誤示范
this.state.comment = 'Hello';

?是應(yīng)該使用 setState() :

// 正確使用
this.setState({comment: 'Hello'});

4.2.State 的更新可能是異步的

出于性能考慮伊脓,React 可能會把多個 setState() 調(diào)用合并成一個調(diào)用府寒。
觀察以下例例子中l(wèi)og的值和button顯示的counter。

import React, {
    Component
} from "react";
export default class SetStatePage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            counter: 0
        };
    }
    changeValue = v => {
        this.setState({
            counter: this.state.counter + v
        });
        console.log("counter", this.state.counter);
    };
    setCounter = () => {
        this.changeValue(1);
        //this.changeValue(2);
        //console.log("counter", this.state.counter);
    };
    render() {
        const {
            counter
        } = this.state;
        return (
            < div >
                <h3 > SetStatePage < /h3> 
                <button onClick = {this.setCounter} >
                    {counter} 
                < /button> 
            </div>
        );
    }
}

如果要獲取到最新狀態(tài)值有以下?式:
1.在回調(diào)中獲取狀態(tài)值

changeValue = v => {
    this.setState({
            counter: this.state.counter + v
        },
        () => {
            console.log("counter", this.state.counter);
        }
    );
};
  1. 使用定時?:
setTimeout(() => {
    this.setCounter();
}, 0);
  1. 原生事件中修改狀態(tài)
componentDidMount(){
  document.body.addEventListener('click', this.changeValue, false)
}

總結(jié): setState只有在合成事件和生命周期函數(shù)中是異步的丽旅,在原生事件和setTimeout中都是同步的椰棘,這里的異步其實是批量更新纺棺。

4.3.State 的更新會被合并

changeValue = v => {
    this.setState({
        counter: this.state.counter + v
    });
};
setCounter = () => {
    this.changeValue(1);
    this.changeValue(2);
};

此時的 this.changeValue(1); 不會生效
如果想要鏈?zhǔn)礁聅tate:

changeValue = v => {
    this.setState(state => ({
        counter: state.counter + v
    }));
};
setCounter = () => {
    this.changeValue(1);
    this.changeValue(2);
};

5.生命周期

參考文檔:https://zh-hans.reactjs.org/docs/react-component.html#constructor

當(dāng)組件實例被創(chuàng)建并插入 DOM 中時榄笙,其生命周期調(diào)用順序如下:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

當(dāng)組件的 props 或 state 發(fā)生變化時會觸發(fā)更新。組件更新的生命周期調(diào)用順序如下:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapsBeforeUpdate()
  • componentDidUpdate()

以下的三個生命周期函數(shù)將被廢棄祷蝌,用getDerivedStateFromProps代替茅撞,目前使用的話加上UNSAFE_:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

引入兩個新的生命周期函數(shù)

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

getDerivedStateFromProps

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 會在調(diào)用 render 方法之前調(diào)用,并且在初始掛載及后續(xù)更新時都會被調(diào)用巨朦。它應(yīng)返回一個對象來更新 state米丘,如果返回 null 則不更新任何內(nèi)容。

請注意糊啡,不管原因是什么拄查,都會在每次渲染前觸發(fā)此方法。這與UNSAFE_componentWillReceiveProps 形成對比棚蓄,后者僅在父組件重新渲染時觸發(fā)堕扶,而不是在內(nèi)部調(diào)用 setState 時。

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate(prevProps, prevState)

在render之后梭依,在componentDidUpdate之前稍算。
getSnapshotBeforeUpdate() 在最近一次渲染輸出(提交到 DOM 節(jié)點)之前調(diào)用。它使得組件能在發(fā)生更改之前從 DOM 中捕獲一些信息(例如役拴,滾動位置)糊探。此生命周期的任何返回值將作為參數(shù)傳遞給 componentDidUpdate(prevProps, prevState, snapshot) 。

6.組件復(fù)合

復(fù)合組件給與你?夠的敏捷去定義?定義組件的外觀和行為河闰,這種?式更明確和安全科平。如果組件間有公?的非UI邏輯,將它們抽取為JS模塊導(dǎo)入使用而不是繼承它姜性。

不具名

新建一個layout.js文件

import React, {
    Component
} from "react";
import TopBar from "../components/TopBar";
import BottomBar from "../components/BottomBar";
export default class Layout extends Component {
    componentDidMount() {
        const {
            title = "商城"
        } = this.props;
        document.title = title;
    }
    render() {
        const {
            children,
            showTopBar,
            showBottomBar
        } = this.props;
        console.log("children", children);
        return ( 
            < div > 
            {showTopBar && <TopBar />}
            {children}
            {showBottomBar && <BottomBar />}
            </div>
        );
    }
}

UserPage.js:

import React, { Component } from "react";
import Layout from "./Layout";
export default class UserPage extends Component {
  render() {
    return (
        <Layout showTopBar={true} showBottomBar={true} title="用戶中心">
            <div>
                <h3>UserPage</h3>
            </div>
        </Layout>
    );
  }
}

具名

類似于Vu中的具名插槽瞪慧,我們傳一個對象進(jìn)去

import React, {
    Component
} from "react";
import Layout from "./Layout";
export default class HomePage extends Component {
    render() {
        return ( < Layout showTopBar = {
                false
            }
            showBottomBar = {
                true
            }
            title = "商城首頁" > {
                /* <div>
                <h3>HomePage</h3>
                </div> */
            } {
                {
                    content: ( 
                    <div >
                        <h3 > HomePage < /h3> 
                    </div>
                    ),
                    txt: "這是個?文本",
                    btnClick: () => {
                        console.log("btnClick");
                    }
                }
            } <
            /Layout>
        );
    }
}

7.redux

在下面的場景中,引入 Redux 是比較明智的

  • 你有著相當(dāng)?量的污抬、隨時間變化的數(shù)據(jù)汞贸;
  • 你的 state 需要有一個單一可靠數(shù)據(jù)來源绳军;
  • 你覺得把所有 state 放在最頂層組件中已經(jīng)?法滿足需要了了。
  • 某個組件的狀態(tài)需要共享矢腻。

redux 是 JavaScript應(yīng)用的狀態(tài)容?门驾,提供可預(yù)測化的狀態(tài)管理。它保證程序行為一致性且易于測試多柑。

安裝redux
npm install redux --save

redux上手

用一個累加?舉例

  1. 需要一個store來存儲數(shù)據(jù)
  2. store里的reducer初始化state并定義state修改規(guī)則
  3. 通過dispatch一個action來提交對數(shù)據(jù)的修改
  4. action提交到reducer函數(shù)里奶是,根據(jù)傳入的action的type,返回新的state

創(chuàng)建store竣灌,src/store/ReduxStore.js

import {
    createStore
} from 'redux'
const counterReducer = (state = 0, action) => {
    switch (action.type) {
        case 'ADD':
            return state + 1
        case 'MINUS':
            return state - 1
        default:
            return state
    }
}
const store = createStore(counterReducer)
export default store

創(chuàng)建ReduxPage

import React, {
    Component
} from "react";
import store from "../store/ReduxStore";
export default class ReduxPage extends Component {
    componentDidMount() {
        store.subscribe(() => {
            console.log("subscribe");
            this.forceUpdate();
            //this.setState({});
        });
    }
    add = () => {
        store.dispatch({
            type: "ADD"
        });
    };
    minus = () => {
        store.dispatch({
            type: "MINUS"
        });
    };
    render() {
        console.log("store", store);
        return ( 
            <div>
                <h3>ReduxPage</h3>
                <p>{store.getState()}</p>
                <button onClick={this.add}>add</button>
                <button onClick={this.minus}>minus</button>
            </div>
        );
    }
}

如果點擊按鈕不能更新聂沙,因為沒有訂閱(subscribe)狀態(tài)變更

還可以在src/index.js的render里訂閱狀態(tài)變更

import store from './store/ReduxStore'
const render = () => {
    ReactDom.render( <
        App / > ,
        document.querySelector('#root')
    )
}
render()
store.subscribe(render)

檢查點

  1. createStore 創(chuàng)建store
  2. reducer 初始化、修改狀態(tài)函數(shù)
  3. getState 獲取狀態(tài)值
  4. dispatch 提交更新
  5. subscribe 變更訂閱

8.react-redux

安裝
npm install react-redux --save

react-redux提供了兩個api

  1. Provider 為后代組件提供store
  2. connect 為組件提供數(shù)據(jù)和變更方法

全局提供store初嘹,index.js

import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import store from './store/'
import { Provider } from 'react-redux'
ReactDom.render(
  <Provider store={store}>
    <App/>
  </Provider>,
document.querySelector('#root')
)

獲取狀態(tài)數(shù)據(jù)及汉,ReactReduxPage.js

import React, {
    Component
} from "react";
import {
    connect
} from "react-redux";
class ReactReduxPage extends Component {
    render() {
        const {
            num,
            add,
            minus
        } = this.props;
        return ( <
            div >
            <
            h1 > ReactReduxPage < /h1> <
            p > {
                num
            } < /p> <
            button onClick = {
                add
            } > add < /button> <
            button onClick = {
                minus
            } > minus < /button> < /
            div >
        );
    }
}
const mapStateToProps = state => {
    return {
        num: state,
    };
};
const mapDispatchToProps = {
    add: () => {
        return {
            type: "add"
        };
    },
    minus: () => {
        return {
            type: "minus"
        };
    }
};
export default connect(
  mapStateToProps, //狀態(tài)映射 mapStateToProps
  mapDispatchToProps, //派發(fā)事件映射
)(ReactReduxPage);

connect中的參數(shù):state映射和事件映射

9.react-router

react-router包含3個庫,react-router屯烦、react-router-dom和react-router-native坷随。react-router提供最基本的路由功能,實際使用的時候我們不會直接安裝react-router驻龟,而是根據(jù)應(yīng)用運行的環(huán)境選擇安裝react-router-dom(在瀏覽?中使用)或react-router-native(在rn中使用)温眉。react-router-dom和react-router-native都依賴react-router,所以在安裝時翁狐,react-router也會?自動安裝类溢,創(chuàng)建web應(yīng)用,
使用:
安裝
npm install --save react-router-dom

基本使用
react-router中奉行一切皆組件的思想露懒,路由?-Router闯冷、鏈接-Link、路由-Route隐锭、獨占-Switch窃躲、重定向-Redirect都以組件形式存在
創(chuàng)建RouterPage.js

import React, {
    Component
} from "react";
import {
    BrowserRouter as Router,
    Route,
    Link
} from "react-router-dom";
export default class RouterPage extends Component {
    render() {
        return ( 
        <div >
            <h3 > RouterPage < /h3> 
            <Router >
                <Link to = "/" > 首頁 < /Link> 
                <Link to = "/user" > 用戶中? < /Link>
                {/* 根路路由要添加exact,實現(xiàn)精確匹配 */ } 
                <Route
                    exact 
                    path = "/"
                    component = {HomePage}
                    //children={() => <div>children</div>}
                    //render={() => <div>render</div>}
                /> 
                <Route 
                path = "/user"
                component = {UserPage}
                /> 
                </Router> 
        </div>
        );
    }
}
class HomePage extends Component {
    render() {
        return ( <
            div >
            <
            h3 > HomePage < /h3> <
            /div>
        );
    }
}
class UserPage extends Component {
    render() {
            return ( <
                div >
                <
                h3 > UserPage < /h3> <
                /div>
            );
    }
}

Route渲染內(nèi)容的三種方式

Route渲染優(yōu)先級:children>component>render钦睡。
這三種方式互斥蒂窒,你只能用一種。
children:func
有時候荞怒,不管location是否匹配洒琢,你都需要渲染一些內(nèi)容,這時候你可以用children褐桌。
除了不管location是否匹配都會被渲染之外衰抑,其它?作?法與render完全一樣

render:func
但是當(dāng)你用render的時候,你調(diào)用的只是個函數(shù)荧嵌。
只在當(dāng)location匹配的時候渲染呛踊。

component: component
只在當(dāng)location匹配的時候渲染砾淌。

404頁面

設(shè)定一個沒有path的路由在路由列表最后面,表示一定匹配

{/* 添加Switch表示僅匹配一個*/ } 
<Switch > 
    {/* 根路由要添加exact谭网,實現(xiàn)精確匹配 */ } 
    <Route
        exact
        path = "/"
        component = {HomePage}
    /> 
    <Route 
        path = "/user"
        component = {UserPage}
    /> 
    <Route 
        component = {EmptyPage}
    /> 
</Switch>
class EmptyPage extends Component {
    render() {
        return ( 
            <div >
                <h3 > EmptyPage - 404 < /h3> 
            </div>
        );
    }
}

10.PureComponent

React.PureComponent與 React.Component很相似汪厨。兩者的區(qū)別在于 React.Component并未實現(xiàn) shouldComponentUpdate(),而 React.PureComponent 中以淺層對比 prop 和 state 的方式來實現(xiàn)了該函數(shù)愉择。
如果賦予 React 組件相同的 props 和 state劫乱,render() 函數(shù)會渲染相同的內(nèi)容,那么在某些情況下使用 React.PureComponent 可提高性能锥涕。

實現(xiàn)性能優(yōu)化
定制了shouldComponentUpdate后的Component

import React, {Component,PureComponent} from "react";
export default class PureComponentPage extends PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            counter: 0,
            // obj: {
            // num: 2,
            // },
        };
    }
    setCounter = () => {
        this.setState({
            counter: 100,
            // obj: {
            // num: 200,
            // },
        });
    };
    render() {
        const {
            counter,
            obj
        } = this.state;
        console.log("render");
        return ( 
        <div >
            <h1 > PuerComponentPage < /h1> 
            <div onClick = {this.setCounter} > counter: {counter} < /div> 
            </div>
        );
    }
}

11.認(rèn)識Hook

Hook 是什么衷戈? Hook 是一個特殊的函數(shù),它可以讓你“鉤入” React 的特性层坠。例如殖妇, useState 是允許你在 React 函數(shù)組件中添加 state 的 Hook。
什么時候會用 Hook窿春? 如果你在編寫函數(shù)組件并意識到需要向其添加一些 state拉一,以前的做法是必須將其它轉(zhuǎn)化為 class。現(xiàn)在你可以在現(xiàn)有的函數(shù)組件中使用 Hook旧乞。

import React, { useState } from "react";
export default function HookPage(props) {
  // 聲明一個叫 “count” 的 state 變量,初始化為0
  const [count, setCount] = useState(0);
  return (
    <div>
        <h3>HookPage</h3>
        <p>{count}</p>
        <button onClick={() => setCount(count + 1)}>add</button>
    </div>
  );
}

使用 Effect Hook

Effect Hook 可以讓你在函數(shù)組件中執(zhí)行副作用操作磅氨。
數(shù)據(jù)獲取尺栖,設(shè)置訂閱以及手動更改 React 組件中的 DOM 都屬于副作用。不管你知不知道這些操作烦租,或是“副作用”這個名字延赌,應(yīng)該都在組件中使用過它們。

import React, { useState, useEffect } from "react";
export default function HookPage(props) {
  // 聲明一個叫 “count” 的 state 變量叉橱,初始化為0
  const [count, setCount] = useState(0);
  // 與 componentDidMount 和 componentDidUpdate相似
  useEffect(() => {
    // 更新 title
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <h3>HookPage</h3>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>add</button>
    </div>
  );
}

在函數(shù)組件主體內(nèi)(這里指在 React 渲染階段)改變 DOM挫以、添加訂閱、設(shè)置定時?窃祝、記錄?志以及執(zhí)行其他包含副作?的操作都是不被允許的掐松,因為這可能會產(chǎn)生莫名其妙的 bug 并破壞 UI 的一致性。
使用 useEffect 完成副作用操作粪小。賦值給 useEffect 的函數(shù)會在組件渲染到屏幕之后執(zhí)行大磺。你可以把 effect 看作從 React 的純函數(shù)式世界通往命令式世界的逃生通道。
默認(rèn)情況下探膊,effect 將在每輪渲染結(jié)束后執(zhí)行杠愧,但你可以選擇讓它 在只有某些值改變的時候 才執(zhí)行。

effect 的條件執(zhí)行

默認(rèn)情況下逞壁,effect 會在每輪組件渲染完成后執(zhí)行流济。這樣的話锐锣,一旦 effect 的依賴發(fā)生變化,它就會被重新創(chuàng)建绳瘟。
然而刺下,在某些場景下這么做可能會矯枉過正。比如稽荧,在上一部分的訂閱示例中橘茉,我們不需要在每次組件更新時都創(chuàng)建新的訂閱,而是僅需要在 source props 改變時重新創(chuàng)建姨丈。
要實現(xiàn)這一點畅卓,可以給 useEffect 傳遞第二個參數(shù),它是 effect 所依賴的值數(shù)組蟋恬。更新后的示例如下:

import React, { useState, useEffect } from "react";
export default function HookPage(props) {
    // 聲明一個叫 “count” 的 state 變量翁潘,初始化為0
    const [count, setCount] = useState(0);
    const [date, setDate] = useState(new Date());
    // 與 componentDidMount 和 componentDidUpdate相似
    useEffect(() => {
        // 更新 title
        document.title = `You clicked ${count} times`;
    }, [count]);
    useEffect(() => {
        const timer = setInterval(() => {
            setDate(new Date());
        }, 1000);
    }, []);
    return (
        <div>
            <h3>HookPage</h3>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>add</button>
            <p>{date.toLocaleTimeString()}</p>
        </div>
    );
}

此時,只有當(dāng) useEffect第二個參數(shù)組里的數(shù)值改變后才會重新創(chuàng)建訂閱歼争。

清除 effect

通常拜马,組件卸載時需要清除 effect 創(chuàng)建的諸如訂閱或計時? ID 等資源。要實現(xiàn)這一點沐绒,useEffect函數(shù)需返回一個清除函數(shù)俩莽,以防止內(nèi)存泄漏,清除函數(shù)會在組件卸載前執(zhí)行乔遮。

useEffect(() => {
    const timer = setInterval(() => {
        setDate(new Date());
    }, 1000);
    return () => clearInterval(timer);
}, []);

12.自定義Hook與Hook使用規(guī)則

有時候我們會想要在組件之間重用一些狀態(tài)邏輯扮超。目前為止,有兩種主流方案來解決這個問題:高階組件和 render props蹋肮。自定義 Hook 可以讓你在不增加組件的情況下達(dá)到同樣的目的出刷。
自定義 Hook 是一個函數(shù),其名稱以 “use” 開頭坯辩,函數(shù)內(nèi)部可以調(diào)用其他的 Hook馁龟。

import React, { useState, useEffect, useMemo } from "react";
export default function CustomHookPage(props) {
    //定義一個叫count的state變量,初始化為0
    const [count, setCount] = useState(0);
    //和didMount漆魔、didUpdate類似
    useEffect(() => {
        console.log("count effect");
        // 只需要在count發(fā)生改變的時候執(zhí)行就可以啦
        document.title = `點擊了了${count}次`;
    }, [count]);
    return (
        <div>
            <h3>自定義Hook</h3>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>add</button>
            <p>{useClock().toLocaleTimeString()}</p>
        </div>
    );
}
//自定義hook坷檩,命名必須以use開頭
function useClock() {
    const [date, setDate] = useState(new Date());
    useEffect(() => {
        console.log("date effect");
        //只需要在didMount時候執(zhí)行就可以了
        const timer = setInterval(() => {
            setDate(new Date());
        }, 1000);
        //清除定時?,類似willUnmount
        return () => clearInterval(timer);
    }, []);
    return date;
}

Hook 使用規(guī)則
Hook 就是 JavaScript 函數(shù)有送,但是使用它們會有兩個額外的規(guī)則:
只能在函數(shù)最外層調(diào)用 Hook淌喻。不要在循環(huán)、條件判斷或者子函數(shù)中調(diào)用雀摘。
只能在 React 的函數(shù)組件中調(diào)用 Hook裸删。不要在其他 JavaScript 函數(shù)中調(diào)用。(還有?個地?方可以調(diào)用 Hook —— 就是自定義的 Hook 中阵赠。)

13.Hook API之useMemo與useCallback

useMemo
把“創(chuàng)建”函數(shù)和依賴項數(shù)組作為參數(shù)傳入 useMemo 涯塔,它僅會在某個依賴項改變時才重新計算memoized 值肌稻。這種優(yōu)化有助于避免在每次渲染時都進(jìn)行高開銷的計算。
···
import React, { useState, useMemo } from "react";
export default function UseMemoPage(props) {
const [count, setCount] = useState(0);
const expensive = useMemo(() => {
console.log("compute");
let sum = 0;
for (let i = 0; i < count; i++) {
sum += i;
}
return sum;
//只有count變化匕荸,這?才重新執(zhí)?
}, [count]);
const [value, setValue] = useState("");
return (
<div>
<h3>UseMemoPage</h3>
<p>expensive:{expensive}</p>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
<input value={value} onChange={event => setValue(event.target.value)} />
</div>
);
}
···
useCallback
把內(nèi)聯(lián)回調(diào)函數(shù)及依賴項數(shù)組作為參數(shù)傳入 useCallback 爹谭,它將返回該回調(diào)函數(shù)的 memoized 版本,該回調(diào)函數(shù)僅在某個依賴項改變時才會更新榛搔。當(dāng)你把回調(diào)函數(shù)傳遞給經(jīng)過優(yōu)化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate )的子組件時诺凡,它將?常有用。
···
import React, { useState, useCallback, PureComponent } from "react";
export default function UseCallbackPage(props) {
const [count, setCount] = useState(0);
const addClick = useCallback(() => {
let sum = 0;
for (let i = 0; i < count; i++) {
sum += i;
}
return sum;
}, [count]);
const [value, setValue] = useState("");
return (
<div>
<h3>UseCallbackPage</h3>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
<input value={value} onChange={event => setValue(event.target.value)} />
<Child addClick={addClick} />
</div>
);
}
class Child extends PureComponent {
render() {
console.log("child render");
const { addClick } = this.props;
return (
<div>
<h3>Child</h3>
<button onClick={() => console.log(addClick())}>add</button>
</div>
);
}
}
···
useCallback(fn, deps) 相當(dāng)于 useMemo(() => fn, deps) 践惑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末腹泌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子尔觉,更是在濱河造成了極大的恐慌凉袱,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侦铜,死亡現(xiàn)場離奇詭異专甩,居然都是意外死亡,警方通過查閱死者的電腦和手機钉稍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門涤躲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嫁盲,你說我怎么就攤上這事篓叶。” “怎么了羞秤?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長左敌。 經(jīng)常有香客問我瘾蛋,道長,這世上最難降的妖魔是什么矫限? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任哺哼,我火速辦了婚禮,結(jié)果婚禮上叼风,老公的妹妹穿的比我還像新娘取董。我一直安慰自己,他們只是感情好无宿,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布茵汰。 她就那樣靜靜地躺著,像睡著了一般孽鸡。 火紅的嫁衣襯著肌膚如雪蹂午。 梳的紋絲不亂的頭發(fā)上栏豺,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音豆胸,去河邊找鬼瘾英。 笑死橄浓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播添谊,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喷市!你這毒婦竟也來了唯沮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤忿檩,失蹤者是張志新(化名)和其女友劉穎尉尾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體燥透,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡沙咏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了班套。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肢藐。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吱韭,靈堂內(nèi)的尸體忽然破棺而出吆豹,到底是詐尸還是另有隱情,我是刑警寧澤理盆,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布痘煤,位于F島的核電站,受9級特大地震影響猿规,放射性物質(zhì)發(fā)生泄漏衷快。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一姨俩、第九天 我趴在偏房一處隱蔽的房頂上張望蘸拔。 院中可真熱鬧,春花似錦环葵、人聲如沸调窍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邓萨。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間先誉,已是汗流浹背湿刽。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褐耳,地道東北人诈闺。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像铃芦,于是被迫代替她去往敵國和親雅镊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容