美文网首页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