react入門三

ReactDOM.render(<MyComponent/>, document.getElementById("root"))發(fā)生了什么

react解析MyComponent標(biāo)簽找到了MyComponent組件舟茶,發(fā)現(xiàn)組件是類定義的亿眠,隨后new出該類的實(shí)例辛萍,并通過(guò)該實(shí)例調(diào)用到原型上的render方法
將render方法返回的虛擬dom轉(zhuǎn)為真實(shí)dom更啄,隨后呈現(xiàn)在頁(yè)面中置鼻,呈現(xiàn)后還會(huì)調(diào)用一個(gè)componentDidMount

ref

  • 字符串的形式(不推薦使用初狰,存在性能問(wèn)題)

  • 回調(diào)的方式
    <input ref={(currentNode) => {this.input = currentNode}}/>籽前, 該回調(diào)一上來(lái)就會(huì)調(diào)用一次
    PS: 向上面這種回調(diào)的方式(內(nèi)聯(lián)的方式)在更新的時(shí)候會(huì)調(diào)用兩次蛙婴,注意是更新的時(shí)候,第一次currentNode為null(因?yàn)槊看蝦ender都會(huì)創(chuàng)建一個(gè)新的內(nèi)聯(lián)函數(shù)舔糖,當(dāng)更新的時(shí)候第一次調(diào)用是為了清空就得函數(shù)娱两,所以返回一個(gè)null),第二次才會(huì)把節(jié)點(diǎn)傳入. 如果非得解決這個(gè)問(wèn)題,react也提供了方案金吗,通過(guò)類綁定的方式:

    <input ref={this.saveInput}/>十兢,

    在class里面定義saveInput

    saveInput = (c) => { // c就是傳入的node節(jié)點(diǎn)
    this.input1 = c;
    }

createRef

class Demo extends React.Compoent{
    myRef = React.createRef(); // createRef返回一個(gè)容器,該容器用于存放被ref所標(biāo)識(shí)的節(jié)點(diǎn)摇庙,該容器專人專用只能綁定一個(gè)旱物,因此后放進(jìn)去的會(huì)覆蓋之前的,如果想使用多個(gè)就需要createRef多次
    render() {
        return (
            <input ref={this.myRef}/> {/*這里相當(dāng)于將input存放到了myRef中了卫袒,input節(jié)點(diǎn)可以通過(guò)this.myRef.current訪問(wèn)*/}
        ) 
    }
}

react中的事件

  • 1 通過(guò)onXXX屬性指定事件處理函數(shù)
    react中使用的是自定義事件(合成事件宵呛,而不是使用原生的dom事件)--- 是為了更好的兼容性

      react中的事件是通過(guò)事件委托的方式處理的(委托給組件最外層元素)--- 事件委托的原理是事件冒泡,使用事件委托高效
    
  • 2 通過(guò)event.target得到發(fā)生事件的dom元素對(duì)象

收集表單數(shù)據(jù)

  • 非受控組件

    現(xiàn)用現(xiàn)取夕凝,通過(guò)ref可以完成

  • 受控組件

    輸入類的元素在隨著輸入將值維護(hù)到狀態(tài)中

高階函數(shù)和柯里化

高階函數(shù):
若函數(shù)A接受的參數(shù)是一個(gè)函數(shù)宝穗,那么A就稱之為高階函數(shù)
若函數(shù)A調(diào)用的返回值依然是一個(gè)函數(shù),那么A就稱之為高階函數(shù)
常見(jiàn)的高階函數(shù):如Promise, setTimeout, 數(shù)組的常用方式
函數(shù)柯里化
部分求值码秉,函數(shù)的調(diào)用仍舊返回函數(shù)逮矛,實(shí)現(xiàn)多次接受參數(shù)最后統(tǒng)一處理的函數(shù)編碼

生命周期

組件從創(chuàng)建到死亡會(huì)經(jīng)歷一些特定的階段

React組件中包含一系列鉤子函數(shù),會(huì)在特定的時(shí)刻調(diào)用

在定義組件時(shí)泡徙,會(huì)在特定的聲明周期回調(diào)函數(shù)中做特定的工作

  • 舊版的生命周期鉤子

組件掛載的執(zhí)行順序
constructor ---> componentWillMount ---> render ---> componentDidMount ---> componentWillUnmount

組件更新執(zhí)行順序(可以分為3條線)

父組件渲染子組件橱鹏,當(dāng)傳入的數(shù)據(jù)更新時(shí)的執(zhí)行路線

componentWillReceiveProps ---> shouldComponentUpdate ---> componenWillUpdate ---> render ---> componentDidUpdate ---> componentWillUnmount

當(dāng)組件setState更新的時(shí)候

setState() ---> shouldComponentUpdate ---> componentWillUpdate ---> render ---> componentDidUpdate ---> componentWillUnmount

當(dāng)強(qiáng)制更新組件的時(shí)候(不更改狀態(tài)數(shù)據(jù)就想讓組件更新就可以走這條線)

forceUpdate() ---> componentWillUpdate ---> render ---> componentDidUpdate ---> componentWillUnmount

ps: shouldComponentUpdate是一個(gè)閥門鉤子,如果不寫這個(gè)鉤子默認(rèn)返回true,接下來(lái)的流程正常執(zhí)行堪藐,如果寫了并且返回false則接下來(lái)的流程不會(huì)執(zhí)行莉兰,只能返回true或者false,

componentWillReciveProps 這個(gè)鉤子第一次不調(diào)用,只有接受的props變化了才會(huì)調(diào)用礁竞,也就是父組件重新渲染導(dǎo)致子組件渲染才執(zhí)行該鉤子糖荒,接受新的props。

總結(jié):

初始化階段
constructor ---> componentWillMount ---> render() ---> componentDidMount
更新階段: 由組件內(nèi)部this.setState() 或者父組件更新觸發(fā)
shouldComponentUpdate ---> componentWillUpdate ---> render ---> componentDidUpdate

卸載階段:由reactDOM.unmountComponentAtNode(node)觸發(fā)
componentWillUnmount()

常用的鉤子:
componentDidMount: 一般做初始化操作例如開(kāi)啟定時(shí)器模捂,發(fā)送網(wǎng)絡(luò)請(qǐng)求捶朵,訂閱消息蜘矢。。综看。

componentWillUnmount: 做一些收尾工作如取消定時(shí)器品腹,取消訂閱等

render: 必須用,需要掉1+n次

  • 新的聲明周期 (react最新版本)

所有帶will的鉤子在新版版中都加了UNSAFE_前綴红碑, 除了卸載的鉤子舞吭,為什么這么做,因?yàn)閞eact在設(shè)計(jì)異步渲染析珊,這些組件可能會(huì)出現(xiàn)bug,以后可能會(huì)被廢棄

新版本廢棄了componentWillMount, componentWillUpdate, componentWillReciveProps,新增了getDerivedStateFromProps 和getSnapshotBeforeUpdate

需要注意的是 getDerivedStateFromProps不能在實(shí)例上調(diào)用羡鸥,需要聲明成靜態(tài)的需要加static, 而且該方法必須要有返回值,要么你返回狀態(tài)對(duì)象忠寻,要么返回null惧浴,不能返回其他的。需要注意的是只要返回一個(gè)對(duì)象奕剃,那么狀態(tài)的更新就沒(méi)有意義了衷旅,因?yàn)橐坏┓祷貭顟B(tài),此狀態(tài)就不能被改了祭饭,該方法接收一個(gè)參數(shù)props, 他會(huì)接收props屬性并且會(huì)派生出一個(gè)取決于props的狀態(tài)芜茵⌒鹆浚可以接收第二個(gè)參數(shù)state,使用場(chǎng)景罕見(jiàn)倡蝙,基本不用。

getSnapshotBeforeUpdate: 該鉤子處于render和componentDidUpdate之間绞佩,用于在更新之前獲取快照寺鸥,因?yàn)楦潞笾暗臓顟B(tài)就不見(jiàn)了,在更新之前再看一眼之前的狀態(tài)品山。

componentDidUpdate() 該鉤子會(huì)接收三個(gè)參數(shù)胆建,第一個(gè)是更新前的props,第二個(gè)是更新前的state,第三個(gè)是一個(gè)快照值(即getSnapshotBeforeUpdate返回的值肘交。)
掛載時(shí):

constructor ---> getDerivedStateFromProps ---> render (react更新dom和refs) ---> componentDidMount

更新:

(new props | setState() | forceUpdate)時(shí) ---> getDerivedStateFromProps(得到一個(gè)派生的狀態(tài)) ---> shouldComponentUpdate

dom的diff算法

class Person extends React.Component{
   state = {
       persons: [
           {id: 2, name: '小張', age: 23},
           {id: 3, name: '小李', age: 19}
       ]
   }

   render() {
       return (
           <ul>
               {
                   this.state.persons.map((person,index) => {
                       return <li key={index}>{person.name}</li>
                   })
               }
           </ul>
       )
   }
}


虛擬dom中的key有什么作用笆载?

簡(jiǎn)單說(shuō)key是虛擬dom對(duì)象的標(biāo)識(shí),在更新顯示是key起著極其重要的作用涯呻,凉驻,

詳細(xì)說(shuō),當(dāng)狀態(tài)中的數(shù)據(jù)發(fā)生變化的時(shí)候复罐,react會(huì)根據(jù)新的數(shù)據(jù)生成新的虛擬dom,隨后react進(jìn)行新虛擬dom和就虛擬dom的diff對(duì)比涝登,比較規(guī)則如下:
- 就虛擬dom中找到了與新虛擬dom中相同的key:
    1) 若虛擬dom中內(nèi)容沒(méi)有變化,直接使用之前的真實(shí)dom
    2) 若虛擬dom中內(nèi)容變了效诅,則生成新的真實(shí)dom胀滚,隨后替換頁(yè)面中之前的真實(shí)dom


- 舊虛擬dom中沒(méi)有找到與新虛擬dom相同的key

    根據(jù)數(shù)據(jù)創(chuàng)建新的真實(shí)dom趟济,隨后渲染到頁(yè)面。

用index作為key可能會(huì)引發(fā)的問(wèn)題:

- 若對(duì)數(shù)據(jù)進(jìn)行逆序添加咽笼,逆序刪除等破壞順序操作顷编,會(huì)產(chǎn)生沒(méi)有必要的真實(shí)DOM更新 ==》 界面效果沒(méi)問(wèn)題,但是效率低

- 如果頁(yè)面中還包含輸入類的dom:
    會(huì)產(chǎn)生錯(cuò)誤dom更新==》 界面有問(wèn)題

注意剑刑,如果不存在對(duì)數(shù)據(jù)逆序添加刪除等破壞順序的操作勾效,僅用于渲染列表,展示列表叛甫,使用index是沒(méi)有問(wèn)題的

<input type="checkbox"/> 這樣的輸入內(nèi)容在react中有一個(gè)defaultChecked屬性层宫,可以代替checked屬性,因?yàn)槭褂胏hecked其监,則必須使用onchange來(lái)修改其值

react組件通訊

  • 父子組件 通過(guò)props可以通信

  • 兄弟組件 可以提取共同狀態(tài)到公共父組件或者使用消息的發(fā)布訂閱萌腿,或者使用redux之類的狀態(tài)管理庫(kù)

消息的發(fā)布訂閱實(shí)現(xiàn)兄弟組件之間的通信常用的三方庫(kù)有pubsubjs

react路由

  • 前端路由的工作原理

    依托的是瀏覽器歷史記錄,可以借助history這個(gè)庫(kù)來(lái)完成操作

  • react-router-dom

    路由組件
    會(huì)自動(dòng)給組件傳入路由相關(guān)的props
    NavLink可以實(shí)現(xiàn)路由的高亮抖苦,通過(guò)activeClassName指定樣式名
    標(biāo)簽體內(nèi)容是一個(gè)特殊的標(biāo)簽屬性
    通過(guò)this.props.children可以獲取標(biāo)簽體內(nèi)容
    路由組件一般放在pages目錄
    一般組件
    一般放在components目錄

    • Switch組件

      該組件會(huì)提高性能毁菱,如果不適用他,那么在匹配到目標(biāo)組件后還會(huì)繼續(xù)往下匹配锌历,使用了他贮庞,當(dāng)匹配到目標(biāo)組件后就不會(huì)網(wǎng)下匹配其他組件了

    • 解決多級(jí)路徑頁(yè)面刷新樣式丟失的問(wèn)題

      • public/index.html中引入樣式時(shí)不寫./而是寫/
      • public/index.html中引入樣式時(shí)不寫./ 而是%PUBLIC_URL%
      • 不要使用HistoryRouter而是使用HashRouter
    • 路由的嚴(yán)格模式與模糊匹配
      不是非必要情況下不要用嚴(yán)格模式,比如有二級(jí)路由的情況下使用了嚴(yán)格模式就會(huì)出問(wèn)題究西,如/home /home/mian 當(dāng)使用了嚴(yán)格模式窗慎, 那么/home/main永遠(yuǎn)匹配不到

    • Redirect的使用

      Redirect放在注冊(cè)路由的最后,當(dāng)所有路由沒(méi)有匹配的時(shí)候提供一個(gè)默認(rèn)的路由

    • 嵌套路由

      • 注冊(cè)子路由的時(shí)候要寫上父路由的path
      • 路由的匹配是按照路由的注冊(cè)順序進(jìn)行的
    • 向路由組件傳遞params

    路由導(dǎo)航向路由組件傳遞params參數(shù)
    <Link to={`home/message/detail/${id}`}>
    // 可以傳遞多個(gè)參數(shù)
    <Link to={`home/message/detail/${id}/${title}`}> // 相當(dāng)于傳遞了一個(gè)id,一個(gè)title
    注冊(cè)的路由
    
    <Route path="/home/message/detail/:id" component={Detail}>
     // 可以接收多個(gè)參數(shù)
    <Route path="/home/message/detail/:id/:title" component={Detail}>
    Detail組件通過(guò)props參數(shù)就能接收到參數(shù)卤材,在接收到的match里面就有傳遞的參數(shù)
    
    
- 向路由組件傳遞search參數(shù)

```
<Link to={`home/message/detail/?id=${id}&title=${title}`}> // 相當(dāng)于傳遞了一個(gè)id,一個(gè)title

接受的方式(無(wú)需聲明正常注冊(cè)路由即可)
<Route path="/home/message/detail" component={Detail}>

Detail組件通過(guò)this.props.location.search獲取遮斥,獲取到的是一個(gè)字符串,我們需要的是對(duì)象的格式扇丛,因此需要特殊處理术吗,安裝腳手架的時(shí)候其實(shí)下載了一個(gè)叫做querystring的庫(kù),通過(guò)這個(gè)庫(kù)可以幫我們完成
import qs from 'querystring';

// 通過(guò)qs.stringify(obj) 可以將一個(gè)對(duì)象轉(zhuǎn)為key=value&key=value的格式
// 通過(guò)qs.parse(str) 可以將一個(gè)key-value格式的字符串變成對(duì)象

```
  • 向路由組件傳遞state參數(shù) (這里的state不是路由狀態(tài)的state)

    無(wú)論是params參數(shù)還是search參數(shù)都在地址欄上帆精,還可以通過(guò)對(duì)象的方式去傳遞较屿,此時(shí)的to屬性只能是一個(gè)對(duì)象,不再是字符串了卓练,
    需要注意的是這種方式傳遞參數(shù)雖然地址欄上沒(méi)有參數(shù)隘蝎,但是刷新的時(shí)候同樣不會(huì)丟失參數(shù),因?yàn)樗谴嬖趆istory中的昆庇,如果是HashRouter刷新會(huì)丟失

```
<Link to={{pathname:`/home/message/detail`, state: {id: id, title: title}}}>

// 接受state
通過(guò)this.props.location.state可以獲取

```

- push 與 replace

歷史記錄是一個(gè)壓棧模式末贾,默認(rèn)是push,如果要開(kāi)啟replace需要這么寫, replace是替換模式
<Link replace to={{pathname:`/home/message/detail`, state: {id: id, title: title}}}>

- 編程時(shí)路由導(dǎo)航

通過(guò)腳本的方式進(jìn)行跳轉(zhuǎn)。路由組件通過(guò)this.props.history可以拿到history對(duì)象整吆,里面包含操作歷史的方法拱撵,需要注意的是編程時(shí)路由跳轉(zhuǎn)時(shí)注冊(cè)路由需要對(duì)應(yīng)辉川,也就是說(shuō)通過(guò)search跳轉(zhuǎn)的不能以params的方式注冊(cè)路由,其他同理拴测,params和query的參數(shù)在地址欄好理解乓旗,state的不再地址欄,但是同理對(duì)應(yīng)的方式也有第二個(gè)參數(shù)可以接收state, 如this.props.history.push(path, state)

編程時(shí)路由導(dǎo)航的幾個(gè)api:
* this.props.history.push
* this.props.history.replace
* this.props.history.goBack
* this.props.history.goForward
* this.props.history.goBack
* this.props.history.go

- withRouter

路由組件里面可以獲取到操作歷史的方法集索,但是再非路由組件里面是沒(méi)有這些的屿愚。可以借助withRouter使一般組件也能獲取到history
import {withRouter} from 'react-router-dom'

比如
export default withRouter(Header) ; 通過(guò)withRouter將一般組件Header加工后暴露务荆,此時(shí)使用header組件就會(huì)有history相關(guān)的內(nèi)容


- BrowserRouter與HashRouter的區(qū)別

 * 底層原理不一樣妆距,BrowserRouter使用的是H5的history API,不兼容Ie9及以下版本HashRouter使用的是url的hash值
 * url表現(xiàn)形式不一樣函匕,HashRouter帶#
 * 刷新后對(duì)路由state的影響
    BrowserRouter沒(méi)有影響娱据,因?yàn)閟tate保存在history對(duì)象中
    HashRouter會(huì)丟失state參數(shù)

* HashRouter可以用于解決一些路徑錯(cuò)誤相關(guān)的問(wèn)題

redux

  • redux是什么

    • 1.1 redux是專門做狀態(tài)管理的庫(kù)不是react插件
    • 1.2 可以用在react,vue,angular中盅惜,但基本與react搭配使用
    • 1.3 專門做集中式狀態(tài)管理中剩,管理應(yīng)用中多個(gè)組件共享的狀態(tài)
  • 什么情況下用redux

    • 1.1 某個(gè)組件的狀態(tài)需要讓其他組件隨時(shí)拿到(共享)
    • 1.2 一個(gè)組件需要改變另一個(gè)組件的狀態(tài)(通信)
  • redux的工作流程

    有一個(gè)核心store,里面的值只能通過(guò)reducer更改,當(dāng)用戶在組件中派發(fā)一個(gè)action的時(shí)候抒寂。action不是特殊的東西结啼,他是一個(gè)帶有type和數(shù)據(jù)的對(duì)象比如{type: "INCREAMENT", data: {count: 1}}就是一個(gè)action,
    通過(guò)dispatch可以將action交給store,store不干活屈芜,他會(huì)讓reducer去更改狀態(tài)郊愧。reduer將狀態(tài)加工完畢會(huì)返回一個(gè)新的狀態(tài)給store。reducer能加工狀態(tài)沸伏,那個(gè)狀態(tài)哪里來(lái)糕珊?其實(shí)reducer干了兩件事,一是初始化狀態(tài)毅糟,二是加工狀態(tài)。action creator是一個(gè)返回action對(duì)象的東西澜公。

  • redux的三個(gè)核心概念

    • 1 action 是一個(gè)動(dòng)作對(duì)象包含兩個(gè)屬性

      • type 標(biāo)識(shí)屬性姆另,值為字符串,唯一坟乾,必要屬性
      • data 數(shù)據(jù)屬性迹辐,值類型任意,可選屬性

      action分同步action(一般對(duì)象) 和異步action(是一個(gè)函數(shù))甚侣,一個(gè)異步的action一定對(duì)應(yīng)一個(gè)同步的action,使用異步action需要一個(gè)中間件明吩,因?yàn)閍ction需要的是一個(gè)對(duì)象,而不是一個(gè)函數(shù)殷费,因此需要安裝redux-thunk,引入后 需要在創(chuàng)建store的時(shí)候作為第二個(gè)參數(shù)傳遞,異步的action是通過(guò)store幫助調(diào)用的

      import {createStore, applyMiddleware} from 'redux'
      import thunk from 'redux-thunk'
      
      createStroe(reducers, applyMiddleware(thunk)) 這里的reducers是個(gè)多個(gè)reducer,以對(duì)象的方式去組織印荔,因?yàn)閷?duì)象的key-value存值和取值更方便低葫。因此reducers的結(jié)構(gòu)應(yīng)該是這樣,是多個(gè)reducer合并的結(jié)果
      
      首先引入一個(gè)函數(shù)將所有的reducer合并為一個(gè)總的reducer
      import {combineReducers} from 'redux'
      const allReducers = combineReducers({
          countRecuder,
          otherReducer,
          ...
      });
      
    • 2 reducer 用于初始化狀態(tài)仍律,加工狀態(tài)嘿悬,加工時(shí)根據(jù)舊的state和action,產(chǎn)生新的state的純函數(shù)

        該函數(shù)接受兩個(gè)參數(shù)水泉,第一個(gè)是之前的狀態(tài)善涨,第二個(gè)是action對(duì)象,初始化的時(shí)候第一個(gè)參數(shù)是undefined草则,在創(chuàng)建store的時(shí)候會(huì)傳遞reducer進(jìn)去钢拧,內(nèi)部會(huì)調(diào)用reducer進(jìn)行狀態(tài)初始化
      
        reducer必須是純函數(shù),純函數(shù)炕横,一個(gè)函數(shù)只要接受同樣的實(shí)參娶靡,那么一定得到同樣的結(jié)果,不能改寫參數(shù)的數(shù)據(jù)
      
        ```
        比如:
        let arr = [1,2,3];
        arr.push(4); 雖然arr的內(nèi)容變了,但是arr的地址引用沒(méi)有變看锉,這樣的變化redux是不認(rèn)的姿锭。這樣寫才可以
      
        [4, ...arr];這樣得到一個(gè)新數(shù)組 
      
        像這樣的函數(shù)不是純函數(shù),同樣的輸入不能得到同樣的輸出
        function demo(a) {
            return Math.random() + a
        }
      
        function demo1(a) { // 改寫了參數(shù)的值
            a = 9
        }
      
        純函數(shù)不會(huì)產(chǎn)生任何副作用伯铣,例如網(wǎng)絡(luò)請(qǐng)求呻此,輸入和輸出設(shè)備,不能調(diào)用Date.now(), Math.random()等不純的方法
        ```
      
    • 3 store 將state,action, reducer聯(lián)系到一起的對(duì)象

      通過(guò)store.getState() 可以得到狀態(tài)

      通過(guò)store.dispatch(action對(duì)象) 可以更改狀態(tài)腔寡,注意不是直接更改焚鲜,其實(shí)每一個(gè)action對(duì)應(yīng)一個(gè)reducer,最終是通過(guò)reducer來(lái)更改,需要注意的是redux只是在維護(hù)狀態(tài)放前,在react中派發(fā)action導(dǎo)致轉(zhuǎn)臺(tái)改變忿磅,是不能引起頁(yè)面的渲染的。因?yàn)閞edux只承諾維護(hù)狀態(tài)凭语,沒(méi)承諾渲染頁(yè)面葱她,如果要渲染需要我們檢測(cè)狀態(tài),手動(dòng)去調(diào)用render. 注意手動(dòng)調(diào)用不是this.render. 可以通過(guò)this.setState({}) 來(lái)重新渲染

      store.subscribe() 可以檢測(cè)redux的狀態(tài)似扔,只要redux中的任意狀態(tài)發(fā)生改變吨些,指定的回調(diào)都會(huì)執(zhí)行,因此頁(yè)面一掛載就可以檢測(cè)redux的狀態(tài)了

      componentDidMount() {
      store.subscribe(() => {
      this.setState({})
      })
      }

      在組件中寫可能寫很多這樣重復(fù)的代碼炒辉,因此可以將檢測(cè)的邏輯放在入口的index.js中豪墅,這樣只要redux的狀態(tài)一發(fā)生變化,真?zhèn)€App就會(huì)重新渲染黔寇,對(duì)應(yīng)的子組件也會(huì)渲染偶器。因?yàn)橛衐iff算法的存在,不會(huì)引起大面積的重繪重排,因此不用擔(dān)心性能問(wèn)題屏轰。

react-redux

react-redux需要注意的幾個(gè)點(diǎn):

  • 所有的ui組件都應(yīng)該包裹一個(gè)容器組件颊郎,他們是父子關(guān)系
  • 容器組件是真正和redux打交道的,里面可以隨意使用redux的api
  • ui組件中不能使用任何redux的api
  • 容器組件會(huì)傳給ui組件redux中所保存的狀態(tài)亭枷,用于操作狀態(tài)的方法袭艺。
  • 容器給ui傳遞狀態(tài),操作狀態(tài)的方法均通過(guò)props傳遞
  • redux的狀態(tài)變化并不會(huì)引起ui的更新叨粘,除非手動(dòng)去檢測(cè)狀態(tài)變化猾编。但是用了react-redux就不需要檢測(cè)也能更新ui, 因?yàn)槭褂胏onnect的時(shí)候他已經(jīng)幫我們檢測(cè)了。

容器組件就是一個(gè)橋梁升敲,用于連接ui組件和redux,因此在容器組件中需要引入要包裝的ui組件答倡,要引入store(redux的核心就是store),需要注意的是容器組件不能親自引入store,而是通過(guò)props的方式傳入store,比如有一個(gè)容器組件Count,此時(shí)可以通過(guò)props傳遞
<Count store={store}/>
需要注意的是頁(yè)面中不止這么一個(gè)容器組件驴党,他都需要store,那么react-redux提供了一個(gè)批量提供store的方法
import {Provider} from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
)

需要引入連接的方式
import {connect} from 'react-redux'
// 得到一個(gè)容器組件
const ContainerComponent = connect()(ui組件);

容器組件和父子組件通信通過(guò)props的方式傳遞狀態(tài)和操作狀態(tài)的方法瘪撇,正常的組件是通過(guò)標(biāo)簽屬性的方式給子組件傳遞參數(shù)的,但是容器組件是由connect方法形成的港庄,并沒(méi)有和ui組件有這明確的父子關(guān)系倔既,因此需要注意的是connect的第一個(gè)()需要兩個(gè)參數(shù)。第一個(gè)參數(shù)是一個(gè)函數(shù)鹏氧,用于生成傳給ui組件的狀態(tài)渤涌,需要返回一個(gè)對(duì)象,既然是操作狀態(tài)把还,那么該函數(shù)一定能接受store中的狀態(tài)实蓬,該函數(shù)接受一個(gè)參數(shù)state,第二個(gè)參數(shù)同樣是一個(gè)函數(shù)吊履,(當(dāng)然也可以是一個(gè)對(duì)象安皱,里面是一系列action,是精簡(jiǎn)版的寫法艇炎,這這種寫法酌伊,react-redux會(huì)自動(dòng)分發(fā)對(duì)應(yīng)的action),接受一個(gè)分發(fā)action的dispatch方法冕臭,返回一個(gè)對(duì)象腺晾,對(duì)象封裝的是操作狀態(tài)的方法。從語(yǔ)義上來(lái)講辜贵,第一個(gè)參數(shù)就是一個(gè)mapStateToProps,第二個(gè)參數(shù)就是mapDispatchToProps

示例代碼

funciton mapStateToProps(state) {
    return {
        count: state
    }
}

function mapDispatchToProps(dispatch) {
    return {
        increament: (number) =>  dispatch(createInreamentAction(number))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(ui組件)

mapStateToProps映射狀態(tài) mapDispatchToProps映射操作狀態(tài)的對(duì)象, 這兩個(gè)函數(shù)都返回一個(gè)對(duì)象
mapDispatchToProps 也可以是一個(gè)對(duì)象归形,對(duì)象是一系列action托慨,react-redux會(huì)自動(dòng)分發(fā)這個(gè)action

注意事項(xiàng):當(dāng)一個(gè)ui組件對(duì)應(yīng)一個(gè)容器組件的時(shí)候,如果這樣編碼會(huì)導(dǎo)致組件的數(shù)量成倍增加暇榴,可以同過(guò)一個(gè)文件將其整合厚棵,定義ui組件蕉世,最終以容器組件暴露

redux的開(kāi)發(fā)工具

一個(gè)項(xiàng)目一般都由多個(gè)人寫,可能都會(huì)用到redux存數(shù)據(jù)婆硬,但是彼此之間不知道狀態(tài)是如何存的狠轻,都存了哪些狀態(tài),這時(shí)可以通過(guò)redux的開(kāi)發(fā)工具來(lái)快速追蹤狀態(tài)的變化彬犯。需要引入一個(gè)庫(kù)redux-devtools-extension
在store.js中引入

import {composeWithDevTools} from redux-devtools-extension

export default createStore(allReducers,composeWithDevTools(applyMiddleware(thunk)))


關(guān)于setState

setState更新?tīng)顟B(tài)的兩種寫法

  • setState(stateChange, [callbacnk]); // 對(duì)象式的寫法向楼,更改狀態(tài)需要手動(dòng)獲取原來(lái)的state
    eg: this.setState({count: this.state.count+1})
  • setState(updater, [callback]); // 函數(shù)式的寫法,不需要獲取原來(lái)的state,因?yàn)闀?huì)作為參數(shù)傳入
    eg: this.setState((state, props) => { // 不僅能拿到state, 還可以拿到props
    return {count: state.count+1}
    })
    兩種寫法都有個(gè)可選的回調(diào),因?yàn)閞eact是異步更細(xì)的谐区。需要注意的是setState是一個(gè)同步方法湖蜕,由setState引起react更新?tīng)顟B(tài)是一個(gè)異步的操作,這個(gè)回調(diào)在狀態(tài)更新完成并且在render后才調(diào)用的.

lazyLoad

路由組件的懶加載宋列,不適用懶加載昭抒,所有的路由組件都會(huì)一次性被加載,如果有成百上千個(gè)路由組件炼杖,這是非趁鸱担恐怖的。路由組件應(yīng)該在跳那個(gè)路由就加載那個(gè)路由組件坤邪,這就是懶加載熙含,在需要的時(shí)候才加載組件

hooks

  • React.useState(狀態(tài)初始值)

    該方法返回一個(gè)數(shù)組,數(shù)組第一位是當(dāng)前狀態(tài)罩扇,第二位是更新?tīng)顟B(tài)的函數(shù)婆芦,因?yàn)閿?shù)組的解構(gòu)賦值是按下標(biāo)來(lái)的,所以如下:

    let [count, setCount] = React.useState(0);

    需要注意的是每次調(diào)用setCount更新喂饥,函數(shù)組件都會(huì)執(zhí)行消约,setCount的參數(shù)可以是一個(gè)函數(shù),

  • React.useEffect(fn员帮,[arr]) 可以在函數(shù)式組件里模擬生命周期鉤子,fn就相當(dāng)于一個(gè)聲明周期鉤子至于是哪一個(gè)需要看情況

    如果不指定第二個(gè)參數(shù)arr,那么所有的狀態(tài)的改變都會(huì)執(zhí)行回調(diào)fn或粮。如果arr是空,表示全都不監(jiān)測(cè)捞高,如果不寫第二個(gè)參數(shù)那么表示全部進(jìn)行監(jiān)測(cè)氯材,如果要監(jiān)聽(tīng)指定的狀態(tài),就需要在數(shù)組中指明硝岗。fn可以返回一個(gè)函數(shù)氢哮。

  • React.useRef()
    和createRef一樣的作用

  • React.createContext

    1. 創(chuàng)建context容器對(duì)象
      const xxxContext = React.createContext();

    2. 渲染子組件時(shí),外面包裹xxxContext.Provider,通過(guò)value屬性給后代組件傳遞數(shù)據(jù)
      <xxxContext.Provider value={數(shù)據(jù)}>
      子組件
      </xxxContext.Provider>

    3)后代組件讀取數(shù)據(jù)
    方式一型檀,僅適用于類組件
    首先聲明接收 static contextType = xxxContext 然后才能通過(guò)this.context.value拿到

      方式二冗尤,函數(shù)式組件和類組件都能接收到
      <xxxContext.Consumer>
          {
    
              value => ( // value就是context中的value數(shù)據(jù)
                  要顯示的內(nèi)容
              )
          }
    
      </xxxContext.Consumer>
    

    注意,在應(yīng)用開(kāi)發(fā)中一般不用context,一般都用它來(lái)封裝react插件。

組件優(yōu)化

- Component的兩個(gè)問(wèn)題
    1) 只要執(zhí)行setState(), 即使不改變狀態(tài)數(shù)據(jù)裂七,組件也會(huì)重新render() 皆看,也就是setState({}) 也會(huì)render.
    2) 只要當(dāng)前組件重新render,就會(huì)自動(dòng)重新render子組件 ,效率低背零。

  

    高效的做法應(yīng)該是
        只有當(dāng)前組件的state或者props數(shù)據(jù)發(fā)生改變時(shí)才重新render

    之所以有這樣的問(wèn)題是因?yàn)镃omponent中的shouldComponentUpdate()默認(rèn)總是返回true. 通過(guò)重寫這個(gè)鉤子可以解決這個(gè)問(wèn)題

    shouldComponentUpdate(nextProps, nextState){
        
        console.log(this.props, this.state) // 當(dāng)前的props和state
        console.log(nextProps, nextState); // 目標(biāo)props和state
        // 根據(jù)當(dāng)前數(shù)據(jù)和目標(biāo)數(shù)據(jù)對(duì)比來(lái)決定返回ture|false ,至于怎么比有人做了腰吟,react中提供了PureComponet這個(gè)組件,他會(huì)重寫 shouldComponentUpdate, 里面的對(duì)比邏輯是寫好了的
        reuturn true;
    }

- PureComponent 

    該組件其實(shí)也沒(méi)干啥徙瓶,就是重寫了shouldComponentUpdate, PureComponent也會(huì)有一點(diǎn)小瑕疵毛雇。因?yàn)樗牡讓舆M(jìn)行了淺對(duì)比,因此如果是下面的這種寫法同樣會(huì)有問(wèn)題

    const obj = this.state;
    obj.xxx = 'XXX'; // 改了某一個(gè)屬性
    this.setState(obj); // 因?yàn)閛bj和state指向了同一個(gè)引用倍啥,因此在使用PureComponent比較時(shí)認(rèn)為state是沒(méi)有變化的禾乘,所以不會(huì)render.因此一定注意使用PureComponent,在更新?tīng)顟B(tài)時(shí)不要和原來(lái)的狀態(tài)發(fā)生任何關(guān)系虽缕。使用時(shí)額外注意數(shù)組的那些方法始藕。

renderProps

場(chǎng)景,比如有兩個(gè)組件A,B氮趋,不應(yīng)該關(guān)系不大伍派,但是因?yàn)槟承┻壿嫷脑蚩赡苄枰@么寫,

<A>
    <B/>
</A>
對(duì)于這種格式要個(gè)可以通過(guò)this.props.children來(lái)獲取剩胁,但是有個(gè)問(wèn)題如果B組件需要A組件內(nèi)的數(shù)據(jù)是做不到的诉植,因此就有了下面的這種格式

<A render={(parmas) => <B {...params}/>}/> // 就相當(dāng)于傳遞了一個(gè)函數(shù),該函數(shù)返回一個(gè)組件昵观,函數(shù)名不一定叫render.

在A組件中預(yù)留一個(gè)位置this.props.render(可以傳遞A中的狀態(tài)到B)晾腔,類似于vue中插槽,

組件通信的總結(jié)

* 組件間的關(guān)系

    1) 父子組件
    2) 兄弟組件 (非嵌套組件)
    3) 祖孫組件(跨級(jí)組件)

* 幾種通信方式

    1) props
        1.1) children props
        1.2) render props

    2) 消息的發(fā)布訂閱
            pubsub ,event等
    3) 集中式管理如redux, dva等

    4) context:
            生產(chǎn)者和消費(fèi)者模式

快速預(yù)覽打包后的程序

全局安裝serve,會(huì)快速開(kāi)啟一個(gè)服務(wù)啊犬。

錯(cuò)誤邊界

一個(gè)頁(yè)面有多個(gè)組件構(gòu)成灼擂,但是其中的某個(gè)組件因?yàn)槟承┎豢煽匾蛩貙?dǎo)致出錯(cuò),此時(shí)整個(gè)頁(yè)面都會(huì)出問(wèn)題觉至,邊界錯(cuò)誤就是處理這類問(wèn)題的剔应,就是當(dāng)某個(gè)組件發(fā)生了錯(cuò)誤,不要導(dǎo)致整個(gè)頁(yè)面崩潰语御,而是將錯(cuò)誤控制在最小的范圍了峻贮,出錯(cuò)了給一個(gè)友好的提示。需要注意的是錯(cuò)誤邊界只有在生產(chǎn)環(huán)境有效应闯,就是打包后才生效纤控,開(kāi)發(fā)環(huán)境是不生效的〉锓模總的來(lái)說(shuō)錯(cuò)誤邊界就是用來(lái)捕獲后代組件錯(cuò)誤嚼黔,渲染出備用頁(yè)面细层,只能捕獲后代組件生命周期產(chǎn)生的錯(cuò)誤惜辑,不能捕獲自己組件產(chǎn)生的錯(cuò)誤和其他組件在合成事件唬涧,定時(shí)器中產(chǎn)生的錯(cuò)誤。

當(dāng)Parent的子組件出錯(cuò)時(shí)會(huì)走這個(gè)鉤子,定義在父組件盛撑,并且攜帶錯(cuò)誤信息err
state = {
    error: '' // 默認(rèn)空碎节,當(dāng)發(fā)生了錯(cuò)誤就會(huì)有值,可以根據(jù)這個(gè)值給一個(gè)友好的提示
}
static getDerivedFromError(err) {
    return {error: err}
}

當(dāng)然還有一個(gè)鉤子componentDidCatch() {
    console.log('渲染組件出錯(cuò)');
} 當(dāng)組件發(fā)生錯(cuò)誤的時(shí)候就會(huì)觸發(fā)該鉤子抵卫,這里可以統(tǒng)計(jì)發(fā)生錯(cuò)誤的次數(shù)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末狮荔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子介粘,更是在濱河造成了極大的恐慌殖氏,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姻采,死亡現(xiàn)場(chǎng)離奇詭異雅采,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)慨亲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門婚瓜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人刑棵,你說(shuō)我怎么就攤上這事巴刻。” “怎么了蛉签?”我有些...
    開(kāi)封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵胡陪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我碍舍,道長(zhǎng)柠座,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任乒验,我火速辦了婚禮愚隧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锻全。我一直安慰自己狂塘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布鳄厌。 她就那樣靜靜地躺著荞胡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪了嚎。 梳的紋絲不亂的頭發(fā)上泪漂,一...
    開(kāi)封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天廊营,我揣著相機(jī)與錄音,去河邊找鬼萝勤。 笑死露筒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的敌卓。 我是一名探鬼主播慎式,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼趟径!你這毒婦竟也來(lái)了瘪吏?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蜗巧,失蹤者是張志新(化名)和其女友劉穎掌眠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體幕屹,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蓝丙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了香嗓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迅腔。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖靠娱,靈堂內(nèi)的尸體忽然破棺而出沧烈,到底是詐尸還是另有隱情,我是刑警寧澤像云,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布锌雀,位于F島的核電站,受9級(jí)特大地震影響迅诬,放射性物質(zhì)發(fā)生泄漏腋逆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一侈贷、第九天 我趴在偏房一處隱蔽的房頂上張望惩歉。 院中可真熱鬧,春花似錦俏蛮、人聲如沸撑蚌。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)争涌。三九已至,卻和暖如春辣恋,著一層夾襖步出監(jiān)牢的瞬間亮垫,已是汗流浹背模软。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留饮潦,地道東北人燃异。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像害晦,于是被迫代替她去往敵國(guó)和親特铝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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