美文网首页react
《深入React技术栈》笔记

《深入React技术栈》笔记

作者: ronniegong | 来源:发表于2017-01-08 22:53 被阅读2069次

有槽先吐

花了几天时间,大致读完了《深入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:https://img.haomeiwen.com/i2898168/9262d2444b223aa5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

相关文章

网友评论

本文标题:《深入React技术栈》笔记

本文链接:https://www.haomeiwen.com/subject/aahcbttx.html