《深入React技術棧》筆記

有槽先吐

花了幾天時間,大致讀完了《深入React技術棧》,簡單總結的話,不及預期。

作者成書前,在知乎開設pure render專欄,更新過一系列react主題文章。看過其中幾篇,認為是內容很不錯的文章,因此對成書期望很高,希望能在書中對react相關內容有一個真正全面深入的理解。

然而,在實際閱讀過程中,總有一種茫然的感覺。個人感覺,書中所述,不見整體,陷入細節。

第三章《解讀React源碼》,書中內容缺少對React整體設計的解構,很快的陷入到細節中,附上一些具體實現的源碼。對于一個本身相對復雜的內容,這種寫法讀完會覺得摸不著頭腦。

寫博文和寫書應當是兩種寫作方法,專欄中的文章,基于作者分享知識,大家的要求不會太高,有一點收貨都會滿意。出版成書,就不能只是將博文整理成冊了,讀者對于書的期望,顯然是比博文更高。

一些收獲

作者對react技術棧,應該是有深入的理解的,不太認可的,是這本書的表述方式。讀過一遍,也有一點收獲。

合成事件與原生事件混用的問題

想實現頁面中有一個二維碼,點擊二維碼不隱藏,點擊二維碼以外地方,隱藏二維碼的功能

//為body綁定原生click
componentDidMount(){
document.body.addEventListener('click',e=>{
  this.setState({active:false})
})
}

//合成事件
handleClickQr(e){
  e.preventDefault();
}

render(){
  return (
    <div classname='code' style={{display:this.state.active ? 'block' : 'none'}} onclick = {this.handleClickQr}>
      ![](qr.jpg)
    </div>
  )
}

預期點擊二維碼時,阻止默認事件,不隱藏二維碼。實際效果是點擊二維碼區域,也會導致隱藏。原因是React合成事件系統的委派機制,事件并沒有綁定到div.qr元素上。

解決方法有兩個

  • 不要將合成事件與原生事件混用
componentDidMount(){
  document.body.addEventListener('click',e=>{
    this.setState({active:false})
  });
  
  document.querySelector('.qr').addEventListener('click',e=>{
    e.preventDefault();
  })
}
  • 通過事件對象e.target判斷
componentDidMount(){
  document.body.addEventListener('click',e=>{
    if(e.target && e.target.matchs('div.code')){return}
    this.setState({active:false})
  });
}

理解setState機制

class Example extends Component {
  constructor(){
    super();
    this.state={val:0};
  }

  componentDidMount(){
    this.setState({val:this.state.val+1});
    console.log(this.state.val);//第一次輸出

    this.setState({val:this.state.val+1});
    console.log(this.state.val);//第二次輸出

    setTimeout(()=>{
      this.setState({val:this.state.val+1});
      console.log(this.state.val);//第三次輸出

      this.setState({val:this.state.val+1});
      console.log(this.state.val);//第四次輸出
    },0)
  }
}

上述代碼,分別輸出:0、0、2、3

理解原因,需知setState機制。

  • setState 底層批量更新
  • 批量更新過程由事務控制
  • 前兩次setState,整個react組件渲染到DOM的過程已經處于一個大的事務中,batchingStrategy的isBatchingUpdates已經被設為true,所以兩次setState的結果并沒有立即生效,而是被放進了dirtyComponents中。
  • setTimeout中執行的兩次setState,因為與初始react組件渲染過程在不同的事件循環,沒有前置的batchedUpdate調用,batchingStrategy的isBatchingUpdates標志位是false,使得新的state立即生效。

redux應用的架構

一個典型的redux應用結構是類型下面

在一些功能簡單的應用中,可以像上面這樣按照類型劃分文件結構。但是在大型應用中,會存在很多組件,如果仍以類型劃分,會導致如actions目錄下,有非常多的action.js文件,很難快速定位文件。因此在大型應用中,可以采用混合方式劃分文件結構。

在上述結構中,首先將redux中的組件,劃分為了三種不同的組件,Layouts、Views、Compoents

  • Layouts是頁面布局組件,描述頁面的基本結構,目的是將主框架與頁面主體內容分離

const Layout = ({children}) => (
<div classname = 'container'>
<Header/>
<div classname = 'content'>
{children}
</div>
</div>
)

* Views是子路由入口組件,描述子路由入口基本結構,包含此路由下所有展示型組件。為了保持子組件的純凈,我們在這一層組件中定義數據和action的入口,將他們分發到子組件中去。Views就是Redux中的容器型組件

@connect(state => {
...
})
class HomeView extends Component {
render(){
const {sth,changeType} = this.props;
const cardProps = {sth,changeType};
return (
<div>
<Card {...cardProps}/>
</div>
)
}
}

* Components就是末級渲染組件,包含了具體的業務邏輯和交互,但是所有的數據和actions都是從Views傳下來的,意味著它們可以完全脫離數據層而獨立存在的展示型組件。

class Card extends Components {
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
}

handleChange(opts){
const {type} = opts;
this.props.changeType(type);
}

render(){
const {sth} = this.props;
return (
<div>
<Switch onChange = {this.handleChange}>
...
</Switch>
{sth}
</div>
)
}
}


理解了三種類型組件,再來看views/和componets文件夾。views文件夾中,存放的每個路由的入口頁,如首頁(Home)。每一個入口都會有三個文件,*.js是入口組件,*.css是樣式,*Redux.js是components/Home文件夾下所有reducer和action的聚合。

在components/Home文件夾里,是當前路由對應的頁面Home需要的所有內容--components、actions、reducers、樣式等。

views/HomeRedux.js示例

import { combineReducers } from 'redux';
// 引入 reducer 及 actionCreator
import list, { loadArticles } from '../components/Home/PreviewListRedux';
export default combineReducers({ list,});
export const actions = { loadArticles,};


components/Home/PreviewListRedux.js示例

const initialState = {
loading: true,
error: false,
articleList: [],
};

const LOAD_ARTICLES = 'LOAD_ARTICLES';
const LOAD_ARTICLES_SUCCESS = 'LOAD_ARTICLES_SUCCESS';
const LOAD_ARTICLES_ERROR = 'LOAD_ARTICLES_ERROR';

export function loadArticles() {
return {
types: [LOAD_ARTICLES, LOAD_ARTICLES_SUCCESS, LOAD_ARTICLES_ERROR],
url: '/api/articles.json',
};
}

export default function previewList(state = initialState, action) {
switch (action.type) {
case LOAD_ARTICLES: {
return {
...state,
loading: true,
error: false,
};
}

case LOAD_ARTICLES_SUCCESS: {
  return {
    ...state,
    loading: false,
    error: false,
    articleList: action.payload,
  };
}

case LOAD_ARTICLES_ERROR: {
  return {
    ...state,
    loading: false,
    error: true,
  };
}

default:
  return state;

}
}


**如果覺得有幫助,可以掃描二維碼對我打賞,謝謝**
![](http://upload-images.jianshu.io/upload_images/2898168-9262d2444b223aa5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一、初入React世界 1.2 JSX語法 class 屬性修改為className for 屬性修改為 htmF...
    不安分的三好份子閱讀 2,213評論 0 4
  • 3. JSX JSX是對JavaScript語言的一個擴展語法, 用于生產React“元素”,建議在描述UI的時候...
    pixels閱讀 2,891評論 0 24
  • 本筆記基于React官方文檔,當前React版本號為15.4.0。 1. 安裝 1.1 嘗試 開始之前可以先去co...
    Awey閱讀 7,807評論 14 128
  • 9.17星期日晴(156) 今天去書店買書,兒子買了一本數獨游戲的書,之前看到五年級的同學玩過,也不知兒子...
    王界程閱讀 172評論 0 0
  • 很長一段時間沒有寫文章了,這篇文章寫于昨天下午,那時的我正坐在海口市中心一家餐廳的窗邊,眺望著這個彌漫著風情的城市...
    卓文業閱讀 315評論 0 1