【react】关于react框架使用的一些细节要点的思考

( _(:3 」∠)_给云友们提个建议,无论是API文档还是书籍,一定要多看几遍!特别是隔一段时间后,会有意想不到的收获的)

这篇文章主要是写关于学习react中的一些自己的思考:

1.setState到底是同步的还是异步的?

2.如何在子组件中改变父组件的state

3.context的运用,避免“props传递地狱”

4.组件类里有私有变量a,它到底改放在this.a中还是this.state对象中(作为属性a)呢?

setState到底是同步的还是异步的?

class MyComponent extends React.Component{
   constructor(props) {
     super(props)
     this.state ={
     value:0
        }
   }
handleClick = () => {
      this.setState({value:1})
           console.log('在handleClick里输出' + this.state.value);
}
render(){
           console.log('在render()里输出' + this.state.value);
return (<div>
          <button onClick ={this.handleClick}>按钮</button>
        </div>)
      }
}
export default MyComponent//省略渲染过程,下面也一样

在这里我们点击按钮时,调用handleClick函数,首先调用this.setState()设置value,随即把this.state.value输出,结果是什么?

你可能会想,这还不简单——“在handleClick里输出1”呗,然而你错了,它的结果为:

事实上,setState()的调用大多数时候是异步的,这意味着,虽然你调用了setState({value:0}),但this.state.value并不会马上变成0,而是直到render()函数调用时,setState()才真正被执行。结合图说明一下:

你可能又会问了:要是我在render()前多次调用this.setState()改变同一个值呢?(比如value)

我们对handleClick做一些修改,让它变得复杂一点,在调用handleClick的时候,依次调用handleStateChange1 ,handleStateChange2,handleStateChange3,它们会调用setState分别设置value为1,2,3并且随即打印

handleStateChange1 = () => {
       this.setState({value:1})
       console.log('在handleClick里输出' + this.state.value);
}
handleStateChange2 = () => {
       this.setState({value:2})
       console.log('在handleClick里输出' + this.state.value);
}
handleStateChange3 = () => {
       this.setState({value:3})
       console.log('在handleClick里输出' + this.state.value);
}
handleClick = () => {
      this.handleStateChange1();
      this.handleStateChange2();
      this.handleStateChange3();
}

那么输出结果会是什么呢?如果setState总是同步调用的,那么结果显然为

在handleClick里输出1

在handleClick里输出2

在handleClick里输出3

但是结果为:,证明它可能是异步的

这下好理解了吧,配合这幅图:

setSate大部分的时候是异步执行的,但是,在react本身监听不到的地方,如原生js的监听里,setInterval,setTimeout里,setState就是同步更新的

关于更多React的异步同步问题请点击这里

如何在子组件中改变父组件的state?

这是我们经常会遇到的问题之一,解决办法是:在父组件中写一个能改变父组件state的方法,并通过props传入子组件中

class Son extends React.Component{
  render(){
       return(<div onClick = {this.props.handleClick}>
                {this.props.value}
              </div>)
          }
}
class Father extends React.Component{
    constructor(props){
          super(props)
          this.state ={
                value:'a'
               }
       }
    handleClick = () => {
         this.setState({value:'b'})
      }
    render(){
         return (<div style ={{margin:50}}>
                     <Son value = {this.state.value} handleClick = {this.handleClick}/>
                 </div>)
         }
}

点击子组件Son,内容由a变成b,说明父组件的state被修改了

context的运用,避免“props传递地狱”

3.1假设一个比较极端的场景:你需要从你的子组件里调用父父父父父组件的属性或方法,怎么办!当组件嵌套层级过深的时候,不断地传props作为实现方式简直就是噩梦!我称之为“props传递地狱”(这个词是我瞎编的,参考自“回调函数地狱”)

我们接下来实现的是这样一个需求,把gene属性(基因)从组件GrandFather -->Father --> Son传递,如果用props传递:

class Son extends React.Component{
  render(){
      return (<h3 style ={{marginTop:30}}>我从我的爷爷那里得到了基因--{this.props.gene}</h3>)
     }
 }
class Father extends React.Component{
  render(){
      return (<Son gene = {this.props.gene}/>)
    }
}
class GrandFather extends React.Component{
  constructor(props) {
     super(props)
     this.state ={
       gene:'[爷爷的基因]'
       }
   }
  render(){
     return (<Father gene = {this.state.gene}/>)
    }
}

demo:

【(。・`ω´・)虽然听起来有点怪怪的但是大家别介意哈】

实现是实现了,但你想想,假设不是从“爷爷”组件,而是从“太太太太爷爷”组件传下来,这多可怕!不过没关系,react提供了一个叫做context(上下文)的API,你在顶层组件的context中定义的属性,可以在所有的后代组件中,通过this.context.属性去引用!让我们一睹为快:

class Son extends React.Component{
   render(){
      console.log(this.context.color);
      return (<h3 style ={{marginTop:30}}>我从我的爷爷那里得到了基因--{this.context.gene}</h3>)
      }
}
Son.contextTypes ={
      gene:React.PropTypes.string
}
class Father extends React.Component{
   render(){
      return (<Son/>)
      }
}
class GrandFather extends React.Component{
   getChildContext(){
      return {gene:'[爷爷的基因]'}
   }
   render(){
      return (<Father />)
   }
}
GrandFather.childContextTypes = {
      gene: React.PropTypes.string
};
export default GrandFather

demo效果同上!这个时候你发现,我们在<GrandFather>组件和<Father>组件中都没有向下传递props,我们就从最下层的Son组件中获取了gene属性,是不是很方便!

解释下代码:

getChildContext()是你在顶层组件中定义的钩子函数,这个函数返回一个对象——你希望在后代组件中取用的属性就放在这个对象中,譬如这个例子中我希望在Son组件中通过this.context.gene取属性,所以在getChildContext()中返回{gene:'[爷爷的基因]'}

GrandFather.childContextTypes和Son.contextTypes 用于规定顶层组件和取顶层组件context的后代组件的属性类型

【注意】GrandFather.childContextTypes和Son.contextTypes 这两个对象必须要规定!否则context只能取到空对象!一开始我犯的这个错误简直让我狂吐三升血。。。。

有图有真相之context和props的区别

3.2context是否推荐使用?

虽然上面这个例子说明了context多么好用,但注意:官方并不推荐经常使用它,因为它会让你的应用架构变得不稳定(官方文档原话If you want your application to be stable, don't use context),在我看来,为什么在大多数情况下要使用props而不是实现数据流呢,因为props凭借组件和组件间严密的逻辑联系,使得你能够清晰地跟踪应用的数据流(it's easy to track the flow of data through your React components with props)当然了,如果你遇到上述的例子的情况,context还是大有裨益的

3.3需要改变context中的属性时候,不要直接改变它,而是使用this.state作为媒介,如果你试图在顶层组件的state中放入一个可变的属性你可以这样做:

getChildContext(){
     return {type:this.state.type}
}

3.4在上述我限制gene的类型时候我是这样写的:gene: React.PropTypes.string,使用了React内置的React.PropTypes帮助属性,此时我的版本为 "react": "15.4.2",在15.5的版本后这一帮助属性被废弃,推荐使用props-types库,像这样:

const PropTypes = require("Prop-Types");
GrandFather.childContextTypes = {
     gene: PropTypes.string
};

当然,在这之前你需要npm install prop-types

组件类里有私有变量a,它到底改放在this.a中还是this.state对象中(作为属性a)呢?

这得根据它是否需要实时的重渲染决定,如果该变量需要同步到变化的UI中,你应该把它放在this.state对象中,如果不需要的话,则把它放在this中(无代码无demo)

【完】--喜欢这篇文章的话不妨关注一下我哟

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据结构与算法

博客园富强民主文明和谐样式

最近很多人问我这个东西是怎么实现的。 那我就分享一下吧 :grin: <!--富强民主点击特效--> <script type="text/javascript...

3338
来自专栏指尖下的Android

OkHttp's NullPointerException in HttpUrl.class

今天测试在小米5.0-6.0的机型中测试发现一个空指针的异常,经过排查后发现OkHttp的请求参数不能为null,这个请求接口会上传当前机型的手机号、ip地址和...

972
来自专栏everhad

虾扯蛋:Android View动画 Animation不完全解析

本文结合一些周知的概念和源码片段,对View动画的工作原理进行挖掘和分析。以下不是对源码一丝不苟的分析过程,只是以搞清楚Animation的执行过程、如何被周期...

1989
来自专栏点滴积累

使用bokeh-scala进行数据可视化

目录 前言 bokeh简介及胡扯 bokeh-scala基本代码 我的封装 总结 一、前言        最近在使用spark集群以及geotrellis框架(...

3428
来自专栏数据小魔方

图表中包含负值的双色填充技巧

今天教大家怎么在Excel里制作带负值的双色填充图表 正负值双色填充 ▼ 通常如果数据中带负值 默认的图表输出虽然能够显示负值 但是负值颜色与正值并没有任何区别...

2126
来自专栏Coding迪斯尼

VUE+WebPack前端游戏设计:实现外星人的动态下滑特效

872
来自专栏MasiMaro 的技术博文

Windows程序设计学习笔记(四)自绘控件与贴图的实现

Windows系统提供大量的控件供我们使用,但是系统提供的控件样式都是统一的,不管什么东西看久了自然会厌烦,为了使界面更加美观,添加一些新的东西我们需要自己绘制...

722
来自专栏郭霖

Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法

大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是最常用的一些用法,这些用法足以覆盖我们平时大多情况下的动画需求了。但是,正如上篇文...

1949
来自专栏极乐技术社区

『组件』大转盘、刮刮乐、老虎机……

组件 框架为开发者提供了一系列基础组件,开发者可以通过组合这些基础组件进行快速开发。 什么是组件: 组件是视图层的基本组成单元。 组件自带一些功能与微信风格的样...

2546
来自专栏Android知识点总结

开源计划之--Android数字运动小插件--NumGo

512

扫码关注云+社区