nativemodal坑 react_react 坑 - CSDN
  • React常见的一些

    2017-03-29 14:26:19
    1、setState()是异步的 this.setState()会调用render方法,但并不会立即改变state的值,state是在render方法中赋值的。所以执行this.setState()后立即获取state的值是不变的。同样的直接赋值state并不会出发更新,...

    1、setState()是异步的
    this.setState()会调用render方法,但并不会立即改变state的值,state是在render方法中赋值的。所以执行this.setState()后立即获取state的值是不变的。同样的直接赋值state并不会出发更新,因为没有调用render函数。

    2、组件的生命周期
    componentWillMount,componentDidMount 只有在初始化的时候才调用。
    componentWillReceivePorps,shouldComponentUpdate,componentWillUpdata,componentDidUpdate 只有组件在更新的时候才被调用,初始化时不调用。

    3、reducer必须返回一个新的对象才能出发组件的更新
    因为在connect函数中会对新旧两个state进行浅对比,如果state只是值改变但是引用地址没有改变,connect会认为它们相同而不触发更新。

    4、无论reducer返回的state是否改变,subscribe中注册的所有回调函数都会被触发。

    5、组件命名的首字母必须是大写,这是类命名的规范。

    6、组件卸载之前,加在dom元素上的监听事件,和定时器需要手动清除,因为这些并不在react的控制范围内,必须手动清除。

    7、按需加载时如果组件是通过export default 暴露出去,那么require.ensure时必须加上default。

    require.ensure([], require => {
        cb(null, require('../Component/saleRecord').default)
    },'saleRecord')

    8、react的路由有hashHistory和browserHistory,hashHistory由hash#控制跳转,一般用于正式线上部署,browserHistory就是普通的地址跳转,一般用于开发阶段。

    9、标签里用到的,for 要写成htmlFor,因为for已经成了关键字。

    10、componentWillUpdate中可以直接改变state的值,而不能用setState。

    11、如果使用es6class类继承react的component组件,constructor中必须调用super,因为子类需要用super继承component的this,否则实例化的时候会报错。

    展开全文
  • 欢迎大家关注【跨平台开发那些事】公众号,定期推...基于最新版本React Native实现JsBundle预加载,界面秒开优化 一、开源库介绍 今年1月份,新开源的react-natvigation库备受瞩目。在短短不到3个月的时间,gith...

     

    欢迎大家关注【跨平台开发那些事】公众号,定期推送跨平台开发技术实践。

     

    上篇博客和大家分享了关于React Native jsBundle预加载,界面启动优化的内容,详情可点击:

    基于最新版本React Native实现JsBundle预加载,界面秒开优化

     

    一、开源库介绍

    今年1月份,新开源的react-natvigation库备受瞩目。在短短不到3个月的时间,github上星数已达4000+。Fb推荐使用库,并且在React Native当前最新版本0.44中将Navigator删除。react-navigation据称有原生般的性能体验效果。可能会成为未来React Native导航组件的主流军。本篇内容基于【 ^1.0.0-beta.9 】版本来介绍关于该库的使用和实战技巧。可以看到,虽然是beta版本,不过基本稳定,大家可放心在项目中使用。奉上  react-navigation 官方文档

    该库包含三类组件:

    (1)StackNavigator:用来跳转页面和传递参数

    (2)TabNavigator:类似底部导航栏,用来在同一屏幕下切换不同界面

    (3)DrawerNavigator:侧滑菜单导航栏,用于轻松设置带抽屉导航的屏幕

     

    二、react-navigation使用

    具体内容大致分为如下:

    (1)react-navigation库属性介绍

    (2)StackNavigator、TabNavigator实现界面间跳转,Tab切换

    (3)StackNavigator界面间跳转、传值、取值

    (4)DrawerNavigator实现抽屉导航菜单

    (5)DrawerNavigator扩展功能

    1、StackNavigator属性介绍

    navigationOptions:配置StackNavigator的一些属性。
    
        title:标题,如果设置了这个导航栏和标签栏的title就会变成一样的,不推荐使用
        header:可以设置一些导航的属性,如果隐藏顶部导航栏只要将这个属性设置为null
        headerTitle:设置导航栏标题,推荐
        headerBackTitle:设置跳转页面左侧返回箭头后面的文字,默认是上一个页面的标题。可以自定义,也可以设置为null
        headerTruncatedBackTitle:设置当上个页面标题不符合返回箭头后的文字时,默认改成"返回"
        headerRight:设置导航条右侧。可以是按钮或者其他视图控件
        headerLeft:设置导航条左侧。可以是按钮或者其他视图控件
        headerStyle:设置导航条的样式。背景色,宽高等
        headerTitleStyle:设置导航栏文字样式
        headerBackTitleStyle:设置导航栏‘返回’文字样式
        headerTintColor:设置导航栏颜色
        headerPressColorAndroid:安卓独有的设置颜色纹理,需要安卓版本大于5.0
        gesturesEnabled:是否支持滑动返回手势,iOS默认支持,安卓默认关闭
     
    
    screen:对应界面名称,需要填入import之后的页面
    
    mode:定义跳转风格
    
       card:使用iOS和安卓默认的风格
    
       modal:iOS独有的使屏幕从底部画出。类似iOS的present效果
    
    headerMode:返回上级页面时动画效果
    
       float:iOS默认的效果
    
       screen:滑动过程中,整个页面都会返回
    
       none:无动画
    
    cardStyle:自定义设置跳转效果
    
       transitionConfig: 自定义设置滑动返回的配置
    
       onTransitionStart:当转换动画即将开始时被调用的功能
    
       onTransitionEnd:当转换动画完成,将被调用的功能
    
    path:路由中设置的路径的覆盖映射配置
    
    initialRouteName:设置默认的页面组件,必须是上面已注册的页面组件
    
    initialRouteParams:初始路由参数

     

    :大家可能对于path不太理解。path属性适用于其他app或浏览器使用url打开本app并进入指定页面。path属性用于声明一个界面路径,例如:【/pages/Home】。此时我们可以在手机浏览器中输入:app名称://pages/Home来启动该App,并进入Home界面。

     

    2、TabNavigator属性介绍

    screen:和导航的功能是一样的,对应界面名称,可以在其他页面通过这个screen传值和跳转。
    
    
    navigationOptions:配置TabNavigator的一些属性
    
    title:标题,会同时设置导航条和标签栏的title
    
    tabBarVisible:是否隐藏标签栏。默认不隐藏(true)
    
    tabBarIcon:设置标签栏的图标。需要给每个都设置
    
    tabBarLabel:设置标签栏的title。推荐
    
    导航栏配置
    
    tabBarPosition:设置tabbar的位置,iOS默认在底部,安卓默认在顶部。(属性值:'top','bottom')
    
    swipeEnabled:是否允许在标签之间进行滑动
    
    animationEnabled:是否在更改标签时显示动画
    
    lazy:是否根据需要懒惰呈现标签,而不是提前,意思是在app打开的时候将底部标签栏全部加载,默认false,推荐为true
    
    trueinitialRouteName: 设置默认的页面组件
    
    backBehavior:按 back 键是否跳转到第一个Tab(首页), none 为不跳转
    
    tabBarOptions:配置标签栏的一些属性iOS属性
    
    activeTintColor:label和icon的前景色 活跃状态下
    
    activeBackgroundColor:label和icon的背景色 活跃状态下
    
    inactiveTintColor:label和icon的前景色 不活跃状态下
    
    inactiveBackgroundColor:label和icon的背景色 不活跃状态下
    
    showLabel:是否显示label,默认开启 style:tabbar的样式
    
    labelStyle:label的样式安卓属性
    
    activeTintColor:label和icon的前景色 活跃状态下
    
    inactiveTintColor:label和icon的前景色 不活跃状态下
    
    showIcon:是否显示图标,默认关闭
    
    showLabel:是否显示label,默认开启 style:tabbar的样式
    
    labelStyle:label的样式 upperCaseLabel:是否使标签大写,默认为true
    
    pressColor:material涟漪效果的颜色(安卓版本需要大于5.0)
    
    pressOpacity:按压标签的透明度变化(安卓版本需要小于5.0)
    
    scrollEnabled:是否启用可滚动选项卡 tabStyle:tab的样式
    
    indicatorStyle:标签指示器的样式对象(选项卡底部的行)。安卓底部会多出一条线,可以将height设置为0来暂时解决这个问题
    
    labelStyle:label的样式
    
    iconStyle:图标样式

     

     

     

    3、DrawerNavigator属性介绍
      

     DrawerNavigatorConfig
    
         drawerWidth - 抽屉的宽度
         drawerPosition - 选项是左或右。 默认为左侧位置
         contentComponent - 用于呈现抽屉内容的组件,例如导航项。 接收抽屉的导航。 默认为DrawerItems
         contentOptions - 配置抽屉内容
    
         initialRouteName - 初始路由的routeName
         order - 定义抽屉项目顺序的routeNames数组。
         路径 - 提供routeName到路径配置的映射,它覆盖routeConfigs中设置的路径。
         backBehavior - 后退按钮是否会切换到初始路由? 如果是,设置为initialRoute,否则为none。 默认为initialRoute行为
    
        DrawerItems的contentOptions属性
    
         activeTintColor - 活动标签的标签和图标颜色
         activeBackgroundColor - 活动标签的背景颜色
         inactiveTintColor - 非活动标签的标签和图标颜色
         inactiveBackgroundColor - 非活动标签的背景颜色
         内容部分的样式样式对象
         labelStyle - 当您的标签是字符串时,要覆盖内容部分中的文本样式的样式对象

    从上述中大致了解了react-navigation三种组件的一些基本属性,所以到我们甩起袖子撸代码见证下奇迹了。
     

    4、使用StackNavigator + TabNavigator实现Tab界面切换、界面间导航

     

     

    API定义:StackNavigator(RouteConfigs, StackNavigatorConfig)、TabNavigator(RouteConfigs, TabNavigatorConfig)

    (1)集成 react-navigation:在终端执行 【 npm install react-navigation --save 】

    (2)界面中导入必要组件:

    import {StackNavigator,TabNavigator,TabBarBottom} from 'react-navigation';
    import HomeScreen from './pages/HomePage';
    import MineScreen from './pages/MinePage';

    (3)定义TabNavigator:

    const Tab = TabNavigator(
      {
        Home:{
          screen:HomeScreen,
          navigationOptions:({navigation}) => ({
            tabBarLabel:'首页',
            tabBarIcon:({focused,tintColor}) => (
              <TabBarItem
                tintColor={tintColor}
                focused={focused}
                normalImage={require('./imgs/nav_fav@2x.png')}
                selectedImage={require('./imgs/nav_fav_actived@3x.png')}
              />
            )
          }),
        },
    
        Mine:{
              screen:MineScreen,
              navigationOptions:({navigation}) => ({
              tabBarLabel:'我',
              tabBarIcon:({focused,tintColor}) => (
                <TabBarItem
                 tintColor={tintColor}
                  focused={focused}
                  normalImage={require('./imgs/tab_me_nor@3x.png')}
                  selectedImage={require('./imgs/tab_me_selected@2x.png')}
                />
              )
            }),
          },
        },
    
        {
          tabBarComponent:TabBarBottom,
          tabBarPosition:'bottom',
          swipeEnabled:false,
          animationEnabled:false,
          lazy:true,
          tabBarOptions:{
            activeTintColor:'#06c1ae',
            inactiveTintColor:'#979797',
            style:{backgroundColor:'#ffffff',},
            labelStyle: {
                  fontSize: 20, // 文字大小
              },
          }
          
        }
    
      );
    

    TabBarItem为封装的组件:

    import React,{Component} from 'react';
    import {Image} from 'react-native';
    
    export default class TabBarItem extends Component {
    
        render() {
            return(
                <Image source={ this.props.focused ? this.props.selectedImage : this.props.normalImage }
                    style={ { tintColor:this.props.tintColor,width:25,height:25 } }
                />
            )
        }
        
    }

    可以看到,我们定义了一个名称为【Tab】的TabNavigator的导航组件。在组件中,分为两层参数:

    (1)第一层参数定义了要切换的界面,即【首页】、【我】两个界面组件,通过screen属性指定。并且通过navigationOptions属性设置相关属性参数。

    (2)设置导航栏的属性参数。

    TabNavigator定义好之后,需要用StackNavigator,顾名思义,StackNavigator就是以栈的方式来存放整个界面的,而TabNavigator是作为一个界面内不同子界面之间切换。所以还需要我们定义StackNavigator:

      const Navigator = StackNavigator(
        
        {
          Tab:{screen:Tab},
          Product:{screen:ProductScreen}
        },
    
        {
          navigationOptions:{
            headerBackTitle:null,
            headerTintColor:'#333333',
            showIcon:true,
           swipeEnabled:false,
           animationEnabled:false,
          },
    
          mode:'card',
        });
    

    看起来和TabNavigator很相似,同样是指定了两个参数:

    (1)指定要跳转的界面组件。同样是screen属性标识界面组件,不多赘述。

    (2)定义跳转属性参数,即顶部导航栏的一些参数设置和跳转方式。

    可以看到,我们将Tab作为一个界面设置到了StackNavigator。这样就可以实现Tab导航和界面间跳转的效果了。

    最后就是在render中引用StackNavigator:

    export default class Demo extends Component {
    
      render() {
            return (
              <Navigator />
            );
      }
    }
    

    StackNavigator还提供了onNavigationStateChange回调方法,用来监听导航状态的改变。具体不再赘述。实现了界面跳转和切换,那么就该来增加下界面之间的感情了,来看看如何实现界面之间的传值和取值。

     


    5、界面间跳转、传值、取值


    在界面组件注入到StackNavigator中时,界面组件就被赋予了navigation属性,即在界面组件中可以通过【this.props.navigation】获取并进行一些操作。

    navigation属性中提供了很多的函数简化界面间操作,简单列举几点:

    (1)通过navigate函数实现界面之间跳转:

     this.props.navigation.navigate('Mine');

    参数为我们在StackNavigator注册界面组件时的名称。同样也可以从当前页面返回到上一页:

    // 返回上一页
    this.props.navigation.goBack();

    (2)跳转时传值:

    this.props.navigation.navigate('Mine',{info:'传值过去'});

    第一个参数同样为要跳转的界面组件名称,第二个参数为要传递的参数,info可以理解为key,后面即传递的参数。

     

    (3)获取值:

    {this.props.navigation.state.params.info}

    通过state.params来获取传来的参数,后面为key值。此处为info。

    以上实现完成,我们就可以愉快的玩耍啦~~ 什么?忽然发现在Android上的效果和IOS效果不一样。老板要界面一致哇~ 怎么办?那就需要我们进行简单的适配了。

     

     

    三、DrawerNavigator实现抽屉导航


    1、导航实现

    API定义:DrawerNavigator(RouteConfigs,DrawerNavigatorConfig)

    (1)界面中定义DrawerNavigator:

     

    import {StackNavigator,TabNavigator,DrawerNavigator} from 'react-navigation';
    import HomeScreen from './pages/HomePage';
    import MineScreen from './pages/MinePage';
    
    export default class Demo extends Component {
    
      render() {
            return (
              <Navigator />
            );
      }
    }
    
    const Navigator = DrawerNavigator({
    
        Home:{screen:HomeScreen},
        Mine:{screen:MineScreen},
    });
    
    const styles = StyleSheet.create({
    
        container: {
            flex: 1,
        },
    });
    
    AppRegistry.registerComponent('Demo', () => Demo);

    定义方式和StackNavigator基本类似,不再赘述。

    (2)HomeScreen界面和MineScreen界面:

     

    export default class HomePage extends Component {
    
        static navigationOptions = {
            drawerLabel: '首页',
            drawerIcon:({tintColor}) => (
                <Image
                    source={require('./../imgs/ic_happy.png')}
                    style={[styles.icon, {tintColor: tintColor}]}/>
            ),
        };
    
        render() {
            return(
                <View style={{flex:1}}>
                    <Text onPress={this._skip.bind(this)}>点击跳转</Text>
                </View>
            );
        }
    
        _skip() {
            this.props.navigation.navigate("Mine");
        }
    }
    
    
    export default class MinePage extends Component {
    
        static navigationOptions = {
            drawerLabel:'我',
             drawerIcon: ({ tintColor }) => (
                <Image
                    source={require('./../imgs/ic_h.png')}
                    style={[styles.icon, {tintColor: tintColor}]}
                />
            ),
        };
    
        render() {
            return(
                <View style={{flex:1}}>
                    <Text onPress={this._skip.bind(this)}>返回上一界面</Text>
                </View>
            );
        }
    
        /**
         * 跳转
         */
        _skip() {
            this.props.navigation.goBack();
        }
    }

    代码很简单,实现了界面之间的跳转。

    2、扩展功能

    (1)默认DrawerView不可滚动。要实现可滚动视图,必须使用contentComponent自定义容器,如下所示:

     

    {
      drawerWidth:200,
      抽屉位置:“对”
      contentComponent:props => <ScrollView> <DrawerItems {... props} /> </ ScrollView>
    }

    (2)可以覆盖导航使用的默认组件,使用DrawerItems自定义导航组件:

     

    import {DrawerItems} from 'react-navigation';
    
    const CustomDrawerContentComponent = (props) => (
      <View style = {style.container}>
        <DrawerItems {... props} />
      </View>  
    );
    

    (3)嵌套抽屉导航
    如果您嵌套DrawerNavigation,抽屉将显示在父导航下方。

     

     

    五、效果图

     

     

     

    抽屉导航:                                                              

                

     

    以上就是我们实战中常用的属性和技巧。具体的操作还需要大家在实践过程中测试体会。

    源码参考

     

    展开全文
  • react-router刷新后报错 之前按照各种教程用BrowserHistory(官方推荐,各种吹)实现了路由跳转 效果如下 结果一刷新就报错了,各种get不到页面….. 然后,查资料说BrowserHistory要配合服务器,然后将项目...

    react-router刷新后报错

    之前按照各种教程用BrowserHistory(官方推荐,各种吹)实现了路由跳转
    效果如下
    这里写图片描述

    结果一刷新就报错了,各种get不到页面…..
    然后,查资料说BrowserHistory要配合服务器,然后将项目打包丢在nginx
    上,然而,,然并卵并没有什么卵用,然后又查了下,说要是
    当nginx找不到页面时让他去找我们配置好的页面

    具体原因:

    之所以你在浏览器内可以由首页跳转到其他路由地址,是因为这是由前端自行渲染的,你在React Router定义了对应的路由,脚本并没有刷新网页访问后台,是JS动态更改了location。当你刷新时,你首先是访问的后台地址,然后返回的页面内加载了React代码,最后在浏览器内执行;也就是说如果这个时候报404,是因为你后台并没有针对这个路由给出返回HTML内容,也谈不上执行React Router了。解决办法就一条:如果你期望所有的路由都由React Router来定义,只有你的后台,无论任何路径,都返回index.html就好了。剩下的事情交给React Router。那么你要做的就是修改后台服务器,可以放在apache,也可以放在你的java路由内做一个通配路径处理。

    解决办法

    如果使用nginx服务器,加上try_files配置:

    location / {
                 ......
                 try_files $uri /index.html;
             }`  

    然后,主页是跳过去了,然而又报错了!

    这里写图片描述

    然后,网上的大神是这样说的
    这里写图片描述

    然后,美滋滋的去试! 然并卵好吗!没什么卵用

    最后没办法 果断放弃BrowserHistory,用了被各种大神嫌弃的要死的hashHistory

    尼玛!真是谁用谁知道啊,特么很好用好不好!

    具体为什么大家都推荐用browserHistory是因为

    首先 browserHistory 其实使用的是 HTML5 的 History API,浏览器提供相应的接口来修改浏览器的历史记录;而 hashHistory 是通过改变地址后面的 hash 来改变浏览器的历史记录;
    History API 提供了 pushState() 和 replaceState() 方法来增加或替换历史记录。而 hash 没有相应的方法,所以并没有替换历史记录的功能。但 react-router 通过 polyfill 实现了此功能,具体实现没有看,好像是使用 sessionStorage。
    另一个原因是 hash 部分并不会被浏览器发送到服务端,也就是说不管是请求 #foo 还是 #bar ,服务只知道请求了 index.html 并不知道 hash 部分的细节。而 History API 需要服务端支持,这样服务端能获取请求细节。
    还有一个原因是因为有些应该会忽略 URL 中的 hash 部分,记得之前将 URL 使用微信分享时会丢失 hash 部分。

    虽然说得很有道理,但我还是坚持用hashHistory,我特么只是想跳转而已

    具体用法

    1,先引入HashRouter

    import {
        Router as HashRouter , // 或者是HashRouter、MemoryRouter
        Route,   // 这是基本的路由块
        Link,    // 这是a标签
        Switch ,  // 这是监听空路由的
        Redirect, // 这是重定向
        Prompt   // 防止转换
    } from 'react-router-dom'

    2 引入createHistory

    import createHistory from 'history/createHashHistory'
    const history = createHistory();

    3 构建router

    ReactDOM.render(
        <HashRouter   history={history}>
            <div className={universal.rowStart} >
                <NavigationBar></NavigationBar>
    
                <Route exact path="/" component={JavaIndex} key={location.key}/>
    
                <Route path="/JavaIndex" component={JavaIndex}/>
    
                <Route path="/JsIndex" component={JsIndex}/>
    
                <Route path="/ProjectIndex" component={ProjectIndex}/>
                <Route path="/PersonIndex" component={PersonIndex}/>
            </div>
        </HashRouter >,

    4 写跳转事件

     history.push(this.props.flag);

    然后就可以了,随你怎么刷新都不会报错了,大家以后遇到坑以后尽量去看看官方文档,mmp,总有些傻逼喜欢复制来复制去,自己不懂在别人的博客上黏贴一段,百度一下,七八篇一模一样的原创博文,

    展开全文
  • 1 React合成事件特点React自己实现了一套高效的事件注册,存储,分发和重用逻辑,在DOM事件体系基础上做了很大改进,减少了内存消耗,简化了事件逻辑,并最大化的解决了IE等浏览器的不兼容问题。与DOM事件体系相比,...

    1 React合成事件特点

    React自己实现了一套高效的事件注册,存储,分发和重用逻辑,在DOM事件体系基础上做了很大改进,减少了内存消耗,简化了事件逻辑,并最大化的解决了IE等浏览器的不兼容问题。与DOM事件体系相比,它有如下特点

    1. React组件上声明的事件最终绑定到了document这个DOM节点上,而不是React组件对应的DOM节点。故只有document这个节点上面才绑定了DOM原生事件,其他节点没有绑定事件。这样简化了DOM原生事件,减少了内存开销
    2. React以队列的方式,从触发事件的组件向父组件回溯,调用它们在JSX中声明的callback。也就是React自身实现了一套事件冒泡机制。我们没办法用event.stopPropagation()来停止事件传播,应该使用event.preventDefault()
    3. React有一套自己的合成事件SyntheticEvent,不同类型的事件会构造不同的SyntheticEvent
    4. React使用对象池来管理合成事件对象的创建和销毁,这样减少了垃圾的生成和新对象内存的分配,大大提高了性能

    那么这些特性是如何实现的呢,下面和大家一起一探究竟。

    2 React事件系统

    先看Facebook给出的React事件系统框图

    Markdown

    浏览器事件(如用户点击了某个button)触发后,DOM将event传给ReactEventListener,它将事件分发到当前组件及以上的父组件。然后由ReactEventEmitter对每个组件进行事件的执行,先构造React合成事件,然后以queue的方式调用JSX中声明的callback进行事件回调。

    涉及到的主要类如下

    ReactEventListener:负责事件注册和事件分发。React将DOM事件全都注册到document这个节点上,这个我们在事件注册小节详细讲。事件分发主要调用dispatchEvent进行,从事件触发组件开始,向父元素遍历。我们在事件执行小节详细讲。

    ReactEventEmitter:负责每个组件上事件的执行。

    EventPluginHub:负责事件的存储,合成事件以对象池的方式实现创建和销毁,大大提高了性能。

    SimpleEventPlugin等plugin:根据不同的事件类型,构造不同的合成事件。如focus对应的React合成事件为SyntheticFocusEvent

    2 事件注册

    JSX中声明一个React事件十分简单,比如

    render() {
      return (
        <div onClick = {
                (event) => {console.log(JSON.stringify(event))}
            } 
        />
      );
    }

    那么它是如何被注册到React事件系统中的呢?

    还是先得从组件创建和更新的入口方法mountComponent和updateComponent说起。在这两个方法中,都会调用到_updateDOMProperties方法,对JSX中声明的组件属性进行处理。源码如下

    _updateDOMProperties: function (lastProps, nextProps, transaction) {
        ... // 前面代码太长,省略一部分
        else if (registrationNameModules.hasOwnProperty(propKey)) {
            // 如果是props这个对象直接声明的属性,而不是从原型链中继承而来的,则处理它
            // nextProp表示要创建或者更新的属性,而lastProp则表示上一次的属性
            // 对于mountComponent,lastProp为null。updateComponent二者都不为null。unmountComponent则nextProp为null
            if (nextProp) {
              // mountComponent和updateComponent中,enqueuePutListener注册事件
              enqueuePutListener(this, propKey, nextProp, transaction);
            } else if (lastProp) {
              // unmountComponent中,删除注册的listener,防止内存泄漏
              deleteListener(this, propKey);
            }
        }
    }

    下面我们来看enqueuePutListener,它负责注册JSX中声明的事件。源码如下

    // inst: React Component对象
    // registrationName: React合成事件名,如onClick
    // listener: React事件回调方法,如onClick=callback中的callback
    // transaction: mountComponent或updateComponent所处的事务流中,React都是基于事务流的
    function enqueuePutListener(inst, registrationName, listener, transaction) {
      if (transaction instanceof ReactServerRenderingTransaction) {
        return;
      }
      var containerInfo = inst._hostContainerInfo;
      var isDocumentFragment = containerInfo._node && containerInfo._node.nodeType === DOC_FRAGMENT_TYPE;
      // 找到document
      var doc = isDocumentFragment ? containerInfo._node : containerInfo._ownerDocument;
      // 注册事件,将事件注册到document上
      listenTo(registrationName, doc);
      // 存储事件,放入事务队列中
      transaction.getReactMountReady().enqueue(putListener, {
        inst: inst,
        registrationName: registrationName,
        listener: listener
      });
    }

    enqueuePutListener主要做两件事,一方面将事件注册到document这个原生DOM上(这就是为什么只有document这个节点有DOM事件的原因),另一方面采用事务队列的方式调用putListener将注册的事件存储起来,以供事件触发时回调。

    注册事件的入口是listenTo方法, 它解决了不同浏览器间捕获和冒泡不兼容的问题。事件回调方法在bubble阶段被触发。如果我们想让它在capture阶段触发,则需要在事件名上加上capture。比如onClick在bubble阶段触发,而onCaptureClick在capture阶段触发。listenTo代码虽然比较长,但逻辑很简单,调用trapCapturedEvent和trapBubbledEvent来注册捕获和冒泡事件。trapCapturedEvent大家可以自行分析,我们仅分析trapBubbledEvent,如下

      trapBubbledEvent: function (topLevelType, handlerBaseName, element) {
        if (!element) {
          return null;
        }
        return EventListener.listen(
          element,   // 绑定到的DOM目标,也就是document
          handlerBaseName,   // eventType
          ReactEventListener.dispatchEvent.bind(null, topLevelType));  // callback, document上的原生事件触发后回调
      },
    
      listen: function listen(target, eventType, callback) {
        if (target.addEventListener) {
          // 将原生事件添加到target这个dom上,也就是document上。
          // 这就是只有document这个DOM节点上有原生事件的原因
          target.addEventListener(eventType, callback, false);
          return {
            // 删除事件,这个由React自己回调,不需要调用者来销毁。但仅仅对于React合成事件才行
            remove: function remove() {
              target.removeEventListener(eventType, callback, false);
            }
          };
        } else if (target.attachEvent) {
          // attach和detach的方式
          target.attachEvent('on' + eventType, callback);
          return {
            remove: function remove() {
              target.detachEvent('on' + eventType, callback);
            }
          };
        }
      },
    

    在listen方法中,我们终于发现了熟悉的addEventListener这个原生事件注册方法。只有document节点才会调用这个方法,故仅仅只有document节点上才有DOM事件。这大大简化了DOM事件逻辑,也节约了内存。

    流程图如下

    Markdown

    3 事件存储

    事件存储由EventPluginHub来负责,它的入口在我们上面讲到的enqueuePutListener中的putListener方法,如下

      /**
       * EventPluginHub用来存储React事件, 将listener存储到`listenerBank[registrationName][key]`
       *
       * @param {object} inst: 事件源
       * @param {string} listener的名字,比如onClick
       * @param {function} listener的callback
       */
      //
      putListener: function (inst, registrationName, listener) {
    
        // 用来标识注册了事件,比如onClick的React对象。key的格式为'.nodeId', 只用知道它可以标示哪个React对象就可以了
        var key = getDictionaryKey(inst);
        var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {});
        // 将listener事件回调方法存入listenerBank[registrationName][key]中,比如listenerBank['onclick'][nodeId]
        // 所有React组件对象定义的所有React事件都会存储在listenerBank中
        bankForRegistrationName[key] = listener;
    
        //onSelect和onClick注册了两个事件回调插件, 用于walkAround某些浏览器兼容bug,不用care
        var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];
        if (PluginModule && PluginModule.didPutListener) {
          PluginModule.didPutListener(inst, registrationName, listener);
        }
      },
    
    var getDictionaryKey = function (inst) {
      return '.' + inst._rootNodeID;
    };

    由上可见,事件存储在了listenerBank对象中,它按照事件名和React组件对象进行了二维划分,比如nodeId组件上注册的onClick事件最后存储在listenerBank.onclick[nodeId]中。

    4 事件执行

    4.1 事件分发

    当事件触发时,document上addEventListener注册的callback会被回调。从前面事件注册部分发现,此时回调函数为ReactEventListener.dispatchEvent,它是事件分发的入口方法。下面我们来详细分析

    // topLevelType:带top的事件名,如topClick。不用纠结为什么带一个top字段,知道它是事件名就OK了
    // nativeEvent: 用户触发click等事件时,浏览器传递的原生事件
    dispatchEvent: function (topLevelType, nativeEvent) {
        // disable了则直接不回调相关方法
        if (!ReactEventListener._enabled) {
          return;
        }
    
        var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent);
        try {
          // 放入批处理队列中,React事件流也是一个消息队列的方式
          ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
        } finally {
          TopLevelCallbackBookKeeping.release(bookKeeping);
        }
    }

    可见我们仍然使用批处理的方式进行事件分发,handleTopLevelImpl才是事件分发的真正执行者,它是事件分发的核心,体现了React事件分发的特点,如下

    // document进行事件分发,这样具体的React组件才能得到响应。因为DOM事件是绑定到document上的
    function handleTopLevelImpl(bookKeeping) {
      // 找到事件触发的DOM和React Component
      var nativeEventTarget = getEventTarget(bookKeeping.nativeEvent);
      var targetInst = ReactDOMComponentTree.getClosestInstanceFromNode(nativeEventTarget);
    
      // 执行事件回调前,先由当前组件向上遍历它的所有父组件。得到ancestors这个数组。
      // 因为事件回调中可能会改变Virtual DOM结构,所以要先遍历好组件层级
      var ancestor = targetInst;
      do {
        bookKeeping.ancestors.push(ancestor);
        ancestor = ancestor && findParent(ancestor);
      } while (ancestor);
    
      // 从当前组件向父组件遍历,依次执行注册的回调方法. 我们遍历构造ancestors数组时,是从当前组件向父组件回溯的,故此处事件回调也是这个顺序
      // 这个顺序就是冒泡的顺序,并且我们发现不能通过stopPropagation来阻止'冒泡'。
      for (var i = 0; i < bookKeeping.ancestors.length; i++) {
        targetInst = bookKeeping.ancestors[i];
        ReactEventListener._handleTopLevel(bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
      }
    }

    从上面的事件分发中可见,React自身实现了一套冒泡机制。从触发事件的对象开始,向父元素回溯,依次调用它们注册的事件callback。

    4.2 事件callback调用

    事件处理由_handleTopLevel完成。它其实是调用ReactBrowserEventEmitter.handleTopLevel() ,如下

      // React事件调用的入口。DOM事件绑定在了document原生对象上,每次事件触发,都会调用到handleTopLevel
      handleTopLevel: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
        // 采用对象池的方式构造出合成事件。不同的eventType的合成事件可能不同
        var events = EventPluginHub.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
        // 批处理队列中的events
        runEventQueueInBatch(events);
      }

    handleTopLevel方法是事件callback调用的核心。它主要做两件事情,一方面利用浏览器回传的原生事件构造出React合成事件,另一方面采用队列的方式处理events。先看如何构造合成事件。

    4.2.1 构造合成事件

      // 构造合成事件
      extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
        var events;
        // EventPluginHub可以存储React合成事件的callback,也存储了一些plugin,这些plugin在EventPluginHub初始化时就注册就来了
        var plugins = EventPluginRegistry.plugins;
        for (var i = 0; i < plugins.length; i++) {
          var possiblePlugin = plugins[i];
          if (possiblePlugin) {
            // 根据eventType构造不同的合成事件SyntheticEvent
            var extractedEvents = possiblePlugin.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
            if (extractedEvents) {
              // 将构造好的合成事件extractedEvents添加到events数组中,这样就保存了所有plugin构造的合成事件
              events = accumulateInto(events, extractedEvents);
            }
          }
        }
        return events;
      },

    EventPluginRegistry.plugins默认包含五种plugin,他们是在EventPluginHub初始化阶段注入进去的,且看代码

      // 将eventPlugin注册到EventPluginHub中
      ReactInjection.EventPluginHub.injectEventPluginsByName({
        SimpleEventPlugin: SimpleEventPlugin,
        EnterLeaveEventPlugin: EnterLeaveEventPlugin,
        ChangeEventPlugin: ChangeEventPlugin,
        SelectEventPlugin: SelectEventPlugin,
        BeforeInputEventPlugin: BeforeInputEventPlugin
      });

    不同的plugin针对不同的事件有特殊的处理,此处我们不展开讲了,下面仅分析SimpleEventPlugin中方法即可。

    我们先看SimpleEventPlugin如何构造它所对应的React合成事件。

      // 根据不同事件类型,比如click,focus构造不同的合成事件SyntheticEvent, 如SyntheticKeyboardEvent SyntheticFocusEvent
    extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
        var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
        if (!dispatchConfig) {
          return null;
        }
        var EventConstructor;
    
       // 根据事件类型,采用不同的SyntheticEvent来构造不同的合成事件
        switch (topLevelType) {
          ... // 省略一些事件,我们仅以blur和focus为例
          case 'topBlur':
          case 'topFocus':
            EventConstructor = SyntheticFocusEvent;
            break;
          ... // 省略一些事件
        }
    
        // 从event对象池中取出合成事件对象,利用对象池思想,可以大大降低对象创建和销毁的时间,提高性能。这是React事件系统的一大亮点
        var event = EventConstructor.getPooled(dispatchConfig, targetInst, nativeEvent, nativeEventTarget);
        EventPropagators.accumulateTwoPhaseDispatches(event);
        return event;
    },
    

    这里我们看到了event对象池这个重大特性,采用合成事件对象池的方式,可以大大降低销毁和创建合成事件带来的性能开销。

    对象创建好之后,我们还会将它添加到events这个队列中,因为事件回调的时候会用到这个队列。添加到events中使用的是accumulateInto方法。它思路比较简单,将新创建的合成对象的引用添加到之前创建好的events队列中即可,源码如下

    function accumulateInto(current, next) {
    
      if (current == null) {
        return next;
      }
    
      // 将next添加到current中,返回一个包含他们两个的新数组
      // 如果next是数组,current不是数组,采用push方法,否则采用concat方法
      // 如果next不是数组,则返回一个current和next构成的新数组
      if (Array.isArray(current)) {
        if (Array.isArray(next)) {
          current.push.apply(current, next);
          return current;
        }
        current.push(next);
        return current;
      }
    
      if (Array.isArray(next)) {
        return [current].concat(next);
      }
    
      return [current, next];
    }

    4.2.2 批处理合成事件

    我们上面分析过了,React以队列的形式处理合成事件。方法入口为runEventQueueInBatch,如下

      function runEventQueueInBatch(events) {
        // 先将events事件放入队列中
        EventPluginHub.enqueueEvents(events);
        // 再处理队列中的事件,包括之前未处理完的。先入先处理原则
        EventPluginHub.processEventQueue(false);
      }
    
      /**
       * syntheticEvent放入队列中,等到processEventQueue再获得执行
       */
      enqueueEvents: function (events) {
        if (events) {
          eventQueue = accumulateInto(eventQueue, events);
        }
      },
    
      /**
       * 分发执行队列中的React合成事件。React事件是采用消息队列方式批处理的
       *
       * simulated:为true表示React测试代码,我们一般都是false 
       */
      processEventQueue: function (simulated) {
        // 先将eventQueue重置为空
        var processingEventQueue = eventQueue;
        eventQueue = null;
        if (simulated) {
          forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseSimulated);
        } else {
          // 遍历处理队列中的事件,
          // 如果只有一个元素,则直接executeDispatchesAndReleaseTopLevel(processingEventQueue)
          // 否则遍历队列中事件,调用executeDispatchesAndReleaseTopLevel处理每个元素
          forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
        }
        // This would be a good time to rethrow if any of the event handlers threw.
        ReactErrorUtils.rethrowCaughtError();
      },

    合成事件处理也分为两步,先将我们要处理的events队列放入eventQueue中,因为之前可能就存在还没处理完的合成事件。然后再执行eventQueue中的事件。可见,如果之前有事件未处理完,这里就又有得到执行的机会了。

    事件执行的入口方法为executeDispatchesAndReleaseTopLevel,如下

    var executeDispatchesAndReleaseTopLevel = function (e) {
      return executeDispatchesAndRelease(e, false);
    };
    
    var executeDispatchesAndRelease = function (event, simulated) {
      if (event) {
        // 进行事件分发,
        EventPluginUtils.executeDispatchesInOrder(event, simulated);
    
        if (!event.isPersistent()) {
          // 处理完,则release掉event对象,采用对象池方式,减少GC
          // React帮我们处理了合成事件的回收机制,不需要我们关心。但要注意,如果使用了DOM原生事件,则要自己回收
          event.constructor.release(event);
        }
      }
    };
    
    // 事件处理的核心
    function executeDispatchesInOrder(event, simulated) {
      var dispatchListeners = event._dispatchListeners;
      var dispatchInstances = event._dispatchInstances;
    
      if (Array.isArray(dispatchListeners)) {
        // 如果有多个listener,则遍历执行数组中event
        for (var i = 0; i < dispatchListeners.length; i++) {
          // 如果isPropagationStopped设成true了,则停止事件传播,退出循环。
          if (event.isPropagationStopped()) {
            break;
          }
          // 执行event的分发,从当前触发事件元素向父元素遍历
          // event为浏览器上传的原生事件
          // dispatchListeners[i]为JSX中声明的事件callback
          // dispatchInstances[i]为对应的React Component 
          executeDispatch(event, simulated, dispatchListeners[i], dispatchInstances[i]);
        }
      } else if (dispatchListeners) {
        // 如果只有一个listener,则直接执行事件分发
        executeDispatch(event, simulated, dispatchListeners, dispatchInstances);
      }
      // 处理完event,重置变量。因为使用的对象池,故必须重置,这样才能被别人复用
      event._dispatchListeners = null;
      event._dispatchInstances = null;
    }

    executeDispatchesInOrder会先得到event对应的listeners队列,然后从当前元素向父元素遍历执行注册的callback。且看executeDispatch

    function executeDispatch(event, simulated, listener, inst) {
      var type = event.type || 'unknown-event';
      event.currentTarget = EventPluginUtils.getNodeFromInstance(inst);
      if (simulated) {
        // test代码使用,支持try-catch,其他就没啥区别了
        ReactErrorUtils.invokeGuardedCallbackWithCatch(type, listener, event);
      } else {
        // 事件分发,listener为callback,event为参数,类似listener(event)这个方法调用
        // 这样就回调到了我们在JSX中注册的callback。比如onClick={(event) => {console.log(1)}}
        // 这样应该就明白了callback怎么被调用的,以及event参数怎么传入callback里面的了
        ReactErrorUtils.invokeGuardedCallback(type, listener, event);
      }
      event.currentTarget = null;
    }
    
    // 采用func(a)的方式进行调用,
    // 故ReactErrorUtils.invokeGuardedCallback(type, listener, event)最终调用的是listener(event)
    // event对象为浏览器传递的DOM原生事件对象,这也就解释了为什么React合成事件回调中能拿到原生event的原因
    function invokeGuardedCallback(name, func, a) {
      try {
        func(a);
      } catch (x) {
        if (caughtError === null) {
          caughtError = x;
        }
      }
    }

    流程图如下

    Markdown

    5 总结

    React事件系统还是相当麻烦的,主要分为事件注册,事件存储和事件执行三大部分。了解了React事件系统源码,就能够轻松回答我们文章开头所列出的React事件几大特点了。

    由于事件系统相当麻烦,文章中不正确的地方,请不吝赐教!

    展开全文
  • react里踩过的那些

    2018-08-01 15:28:25
    本文根据react+antd+redux模式,记录官方不给的小内容。 input的只读模式: 注意:是readOnly,readonly或readonly=”readonly”无效 不带毫秒数的时间戳,转换前要unix为标准格式, moment.unix(int类型),多个...

    本文根据react+antd+redux模式,记录官方不给的小内容。

    input的只读模式

    <Input readOnly />  

    注意:是readOnly,readonly或readonly=”readonly”无效。


    不带毫秒数的时间戳
    format前要先unix为标准格式, moment.unix(int类型)。
    多个时间方法调用:

    const formatDate = (timeTs, formatter = 'YYYY-MM-DD HH:mm:ss') => (
        !timeTs ? '' : moment.unix(parseInt(timeTs, 10)).format(formatter)
    );
    const planEndTime=formatDate(stateSit.planEndTime);

    Error: must set key for children
    在使用antd-design中的select的组件时候,报这样的错误。
    原因:在select中设置了多选mode = ‘multiple’,并把initialValue或value的值设为了[”];
    解决方法:initialValue或value值设为空[],或不为空的字符串[“xxx”];


    Warning: getFieldDecorator will override value, so please don’t set value directly and use setFieldsValue to set it.
    在使用antd-design中的From组件用getFieldDecorator方式的时候,报这样的警告。
    原因:因为我们在自定义组件中定义了value、defaultValue值,getFieldDecorator会覆盖我们定义的值;
    解决方法:不能用控件的 value defaultValue 等属性来设置表单域的值,默认值可以用 getFieldDecorator 里的 initialValue;


    页面跳转传参,this.props.location.query和this.props.location.state的区别
    this.props.location.query:跳转后的url中含有传过来的信息;无跳转刷新页面的时候,无需判断是否有传参。
    this.props.location.state:跳转后的url中不含有传过来的信息;无跳转刷新页面的时候,需要判断是否有传参,否则容易报错。


    数组的push和concat方法
    push:将一个对象添加到某数组的最后。
    concat:两个或多个数组拼接用,将数组添加到某数组的最后,不会过滤掉重复值。

    var arr = [1,2,3];
    arr = arr.concat( [ 4,5,6,3] ); 
    console.log( arr ); // 输出 [1,2,3,4,5,6,3];
    
    如果你的环境支持 ES6还可以用 ... 
    
    var arr = [ 1,2,3, ...arr2 ];
    或者 
    
    var arr = [ 1, 2, 3 ];
    arr.push( ...[ 4, 5, 6 ] );

    将数组按照某个属性进行分类显示,groupBy 与map合作
    groupBy :http://www.css88.com/archives/7774
    项目实际代码段:

    import { groupBy } from 'lodash';
    const userContent = stateSit.participants ? Object.values(groupBy(stateSit.participants, 'teamId'))
            .map((men, i) => `${men[0].team}:${men.map(({ employeeId, employeeName }, idx) => employeeName).join(',')}`)
            .join(';') : null;

    项目实际代码段运行结果:
    这里写图片描述


    绑定的函数在加载时就会马上执行,不是触发才执行
    写法规范例子:

    不会马上执行:
    <div onClick={ handlerClick }>点击我呀!</div>
    有传入参数,加载时马上执行:
    <div onClick={ handlerClick(i) }>点击我呀!</div>
    解决方法:
    <div onClick={ ()=>{ handlerClick(i) } }>点击我呀!</div> 

    Uncaught TypeError: Cannot call a class as a function
    报错这样的错,请确保你的Component继承了React.Component,否则你使用React语法就会出错。


    向一个对象数组里面添加新的属性
    Object.assign() :用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

    var arry= [{a:11,b:22,c:33,d:44},{a:11,b:0,c:0,d:44},{a:11,b:22,c:99,d:99}];
    var arry2=[];
    arry.map(((item, index)=> {
         arry2.push(Object.assign({},item,{mess1:item.c,mess2:item.d}))
    }))
    console.log(arry2);

    代码执行结果:
    这里写图片描述


    将一个对象数组数据拿出来变成另一个对象
    Object.assign() :用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

    var arry= [{a:11,b:22,c:33,d:44},{a:11,b:0,c:0,d:44},{a:11,b:22,c:99,d:99}];
    var arry2=[];
    arry.map(((item, index)=> {
         arry2.push(Object.assign({},{mess1:item.c,mess2:item.d}))
    }))
    console.log(arry2);

    代码执行结果:
    这里写图片描述


    不要轻易用Async/Await调戏React的生命周期componentWillMount
    Async/Await是异步执行,而componentWillMount我个人认为是同步执行,改为异步的话,虽然不会造成阻塞,但会影响页面数据渲染。比B是由生命周期里的A计算得出的,A却是异步获取的,这样逻辑就不通,因为A正在发送请求的同时,B已经开始执行计算了。应该将A改为callback获取结果。

    展开全文
  • React 脚手架 create-react-app 新版使用说明 重点是配置代理 近期更新了一下 create-react-app 工具,然后发现,和原来的老版本使用出现了略微的差异。比如原先想要处理 sass 还需要去手动配置 webpack 但是新版...
  • React-Native遇到那些

    2018-10-31 11:37:10
    目录 搭建环境 1、在模拟器上都安装不上package 2、安装JDK竟然有问题 3、Unable to load script from ...4、ReactNative:The development server returned response error code: 500 5、Unable to resolve mo...
  • 现在,react已经慢慢退火,该用用react技术栈的已经使用上,填过多少,加过多少班,血泪控诉也不下千文。 今天,再谈一遍react优势,WTF? React的收益有哪些?React的优势是什么?react和vue、angularJS等其它...
  • 刚开始学习react,遇到一些小问题。我使用的是create-react-app脚手架生成的项目。这个脚手架一键生成react项目,非常方便。先简单记录一下这个新建项目的过程。一、打包1、安装使用npm install -g create-react-app...
  • 为什么要学习 React? 首先,React 相较于其他框架,其生态圈发展最为完整成熟,有非常多现成的、完整的解决方案。 其次,它适用于大中型应用的开发,便于团队中多人之间协作,很多大厂都在正式的项目中使用了 React...
  • 随着项目对react native的使用,碰到了很多RN的。。。只可惜我不是职业前端,让我一个php来做页面,是在是有些勉强了。。这篇主要是实现RN的单选按钮,上传表单的时候使用的。github地址:...
  • 1. 从后台传回来的日期格式可能是这样(2018-05-04 13:20:09),“-”这个可能转换时这个时间就会变空。所以我们要这样做:new Date(data.curriculaTime.replace(/-/g, "/")); ——————————...
  • 距离上一次提及React的内容差不多也快1年多了,然后这一年里平胸而论觉得进步不多,然后最近因为业务需要然后都在做一些前端的工作。 然后以前都是从npm init一路自己撸到配置package.json到写scripts那些,现在...
  • React事务机制解析

    2019-01-11 23:28:57
    react有一定了解的开发人员应该都听说过react的事务机制,这个机制几乎贯穿于react所有提供的方法,包括react中最常使用的setState函数 那么,react的事务机制到底是一种什么样的机制呢,为了解释事务机制的实现...
  • What’s ReactReact 是 facebook 開發的一個 JS 函式庫,負責產生與管理前端的 UI 。它並不算框架。 Why React? 用純 JS 在前端產生 HTML (一般來說是在後端產生 HTML 送到前端) 使用 Virtual DOM,重繪時...
  • react-native

    2018-03-02 00:42:51
    react-native this绑定 如果自定义函数中需要调用state和props属性或者其他函数需要用到this时,必须在onPress里的函数中绑定this,形如: this.onSubmit.bind(this) &lt;Text style={styles.titleText...
  • React 专注于 view 层,组件化则是 React 的基础,也是其核心理念之一,一个完整的应用将由一个个独立的组件拼装而成。 1. ES5写法React.createClass, 2 . ES6写法React.Component, 3. 无状态的函数式写法(纯...
  • 转载请注明出处:王亟亟的大牛之路继续本周的大方向,继续学习React,昨天把简单的hi all内容呈现出来后,今天研究如何多页面或者实现页面嵌套, 开始今天的内容前老规矩,先安利:...
  • 今天学习了轮播图的使用,上网查阅了一下,发现有react-native-swiper和React-Native-Viewpager 两种封装的比较好的第三方组件,对比了下文档,觉得react-native-swiper功能更加完善,而且文档说明比较全面,所以...
  • 一来就入了深呢,React系。。。 记录两个点,开发环境配置,React本身。 1,开发环境配置 由于React中可以使用jsx语法,也为了方便以后开发调试,需要装好些个东西,配置环境等等。。。 nodejs必须要装,基本...
1 2 3 4 5 ... 20
收藏数 217,172
精华内容 86,868
关键字:

nativemodal坑 react