平时写react 小技巧

  • Stateless function 无状态组件平时写组件用到比较多的就是无状态组件,不但优雅,也是优化react性能的一种手段。
      const Greeting = ({ name, style }) => {
        return <div style={style}>{name}</div>
      };
  • Array as children 把数组数据渲染出来

    经常会遇到处理数组数据的情况,可以用下面的方式简单的渲染出来。

      render() {
          return (
              (<ul>
                  {List.map((item) => (
                       <li>{item}</li>
                  ))}
              </ul>)
          )     
      }
  • 封装基础类组件

    比如 <input type="text" > 每次写很麻烦吧,可以封装一个成一个组件

      const input = (props) => {
          return <input type = {props.type} {...props} />
      }
  • Layout Component 布局组件

    组件可以分成很多类类,有的是布局类,有的是功能类。下面是一种布局类的组件。

      <FlexContainer>
        <div style={{ flex: 1 }}>{this.props.leftSide}</div>
        <div style={{ flex: 2 }}>{this.props.rightSide}</div>
      </FlexContainer>
  • Higher Order Component 高阶组件

    高阶组件很像decorator,提升组件的能力。比如你想一些组件里面使用一下功能,react-router 中

      import { withRouter } from 'react-router'
      withRouter(SomeComponent)

    例子:

      var Enhance = ComposedComponent => class extends React.Component {
        componentDidMount() {
          this.setState({ name: "李狗子" });
        }
        render() {
          return <ComposedComponent {...this.props} name = {this.state.name} />;
        }
      };
  • 受控组件,不受控组件

    项目中经常会用到这两种情况如:
    受控组件,更新的时候需要使用this.setState

      constructor() {
          super();
          this.state = {value: ""}
      }
      render() {
          return <input type="text" value={this.state.value} />
      }

    不受控组件,主要需要通过ref来获取input的值。

      render() {
          return <input type="text" ref="myInput" />
      }

    两种方法都可以在特定的场合去使用,个人觉得数据相对重要的页面需要使用受控组件会比较合适。

  • 使用三元表达式

    项目中经常有判断语句,用三元表达式可以很方便的写出想要的逻辑

      const demo = ({ isOK }) => {
          return isOK 
          ? <p> Yes </p> 
          : <p> No </p>
      };
  • 给setState传入function

    可以使用function来更新state

      this.setState((prevState, props) => ({
          return ...
      }));
  • 通过ref属性获取component

    场景:下面的例子是初始化组件后,让input默认获取光标。ref最终指向的已经渲染好的DOM节点,或者是react class的实例。具体可以看官方的文档

      componentDidMount() {
          this.input.focus();
      }
      render() {
          return (
              <input
                ref={comp => { this.input = comp; }}
              />
          )
      }
  • 切勿使用...props传递数据

    一个非常错误的做法比如:

      <Component {...props} />

    props上面如果有非常多的属性,会造成非常昂贵的计算。正确的应该

      <Component name = { props.name } />

以上是平时写React用到的一些写法小技巧,说有用还蛮有用的!

有错误的地方还请指正!谢谢大家。

下面2个链接都很棒哦!记得收藏star...

参考:

https://github.com/vasanthk/react-bits

react 代码规范

https://github.com/airbnb/javascript/tree/master/react

 

 

dangerouslySetHTML 和 style 属性

 

dangerouslySetHTML

出于安全考虑的原因(XSS 攻击),在 React.js 当中所有的表达式插入的内容都会被自动转义,就相当于 jQuery 里面的 text(…) 函数一样,任何的 HTML 格式都会被转义掉:

class Editor extends Component {
  constructor() {
    super()
    this.state = {
      content: '<h1>React.js 小书</h1>'
    }
  }

  render () {
    return (
      <div className='editor-wrapper'>
        {this.state.content}
      </div>
    )
  }
}

假设上面是一个富文本编辑器组件,富文本编辑器的内容是动态的 HTML 内容,用 this.state.content 来保存。我希望在编辑器内部显示这个动态 HTML 结构,但是因为 React.js 的转义特性,页面上会显示:

表达式插入并不会把一个 <h1> 渲染到页面,而是把它的文本形式渲染了。那要怎么才能做到设置动态 HTML 结构的效果呢?React.js 提供了一个属性 dangerouslySetInnerHTML,可以让我们设置动态设置元素的 innerHTML:

...
  render () {
    return (
      <div
        className='editor-wrapper'
        dangerouslySetInnerHTML={{__html: this.state.content}} />
    )
  }
...

需要给 dangerouslySetInnerHTML 传入一个对象,这个对象的 __html 属性值就相当于元素的 innerHTML,这样我们就可以动态渲染元素的 innerHTML 结构了。

有写朋友会觉得很奇怪,为什么要把一件这么简单的事情搞得这么复杂,名字又长,还要传入一个奇怪的对象。那是因为设置 innerHTML 可能会导致跨站脚本攻击(XSS),所以 React.js 团队认为把事情搞复杂可以防止(警示)大家滥用这个属性。这个属性不必要的情况就不要使用。

style

React.js 中的元素的 style 属性的用法和 DOM 里面的 style 不大一样,普通的 HTML 中的:

<h1 style='font-size: 12px; color: red;'>React.js 小书</h1>

在 React.js 中你需要把 CSS 属性变成一个对象再传给元素:

<h1 style={{fontSize: '12px', color: 'red'}}>React.js 小书</h1>

style 接受一个对象,这个对象里面是这个元素的 CSS 属性键值对,原来 CSS 属性中带 - 的元素都必须要去掉 - 换成驼峰命名,如 font-size 换成 fontSizetext-align 换成 textAlign

用对象作为 style 方便我们动态设置元素的样式。我们可以用 props 或者 state中的数据生成样式对象再传给元素,然后用 setState 就可以修改样式,非常灵活:

<h1 style={{fontSize: '12px', color: this.state.color}}>React.js 小书</h1>

只要简单地 setState({color: 'blue'}) 就可以修改元素的颜色成蓝色。

 

 

 

 

Prop 验证

随着应用不断变大,保证组件被正确使用变得非常有用。为此我们引入propTypesReact.PropTypes 提供很多验证器 (validator) 来验证传入数据的有效性。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。注意为了性能考虑,只在开发环境验证 propTypes。下面用例子来说明不同验证器的区别:

React.createClass({
  propTypes: {
    // 可以声明 prop 为指定的 JS 基本类型。默认
    // 情况下,这些 prop 都是可传可不传的。
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,

    // 所有可以被渲染的对象:数字,
    // 字符串,DOM 元素或包含这些类型的数组。
    optionalNode: React.PropTypes.node,

    // React 元素
    optionalElement: React.PropTypes.element,

    // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
    optionalMessage: React.PropTypes.instanceOf(Message),

    // 用 enum 来限制 prop 只接受指定的值。
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

    // 指定的多个对象类型中的一个
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),

    // 指定类型组成的数组
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

    // 指定类型的属性构成的对象
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

    // 特定形状参数的对象
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),

    // 以后任意类型加上 `isRequired` 来使 prop 不可空。
    requiredFunc: React.PropTypes.func.isRequired,

    // 不可空的任意类型
    requiredAny: React.PropTypes.any.isRequired,

    // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接
    // 使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    }
  },
  /* ... */
});
  static get propTypes () {
    return {
      todoLeft: PropTypes.number.isRequired,
      actions: PropTypes.object.isRequired,
      filter: PropTypes.string.isRequired
    }
  }
  ...
    <strong>{this.props.todoLeft}</strong>
  ...

 

默认 Prop 值

React 支持以声明式的方式来定义 props 的默认值。

var ComponentWithDefaultProps = React.createClass({
  getDefaultProps: function() {
    return {
      value: 'default value'
    };
  }
  /* ... */
});

当父级没有传入 props 时,getDefaultProps() 可以保证 this.props.value 有默认值,注意 getDefaultProps 的结果会被 缓存。得益于此,你可以直接使用 props,而不必写手动编写一些重复或无意义的代码。

传递 Props:小技巧

有一些常用的 React 组件只是对 HTML 做简单扩展。通常,你想少写点代码来把传入组件的 props 复制到对应的 HTML 元素上。这时 JSX 的 spread 语法会帮到你:

var CheckLink = React.createClass({
  render: function() {
    // 这样会把 CheckList 所有的 props 复制到 <a>
    return <a {...this.props}>{'√ '}{this.props.children}</a>;
  }
});

React.render(
  <CheckLink href="/checked.html">
    Click here!
  </CheckLink>,
  document.getElementById('example')
);

单个子级

React.PropTypes.element 可以限定只能有一个子级传入。

var MyComponent = React.createClass({
  propTypes: {
    children: React.PropTypes.element.isRequired
  },

  render: function() {
    return (
      <div>
        {this.props.children} // 有且仅有一个元素,否则会抛异常。
      </div>
    );
  }

});

Mixins

组件是 React 里复用代码最佳方式,但是有时一些复杂的组件间也需要共用一些功能。有时会被称为 跨切面关注点。React 使用 mixins 来解决这类问题。

一个通用的场景是:一个组件需要定期更新。用 setInterval() 做很容易,但当不需要它的时候取消定时器来节省内存是非常重要的。React 提供 生命周期方法 来告知组件创建或销毁的时间。下面来做一个简单的 mixin,使用 setInterval() 并保证在组件销毁时清理定时器。

var SetIntervalMixin = {
  componentWillMount: function() {
    this.intervals = [];
  },
  setInterval: function() {
    this.intervals.push(setInterval.apply(null, arguments));
  },
  componentWillUnmount: function() {
    this.intervals.map(clearInterval);
  }
};

var TickTock = React.createClass({
  mixins: [SetIntervalMixin], // 引用 mixin
  getInitialState: function() {
    return {seconds: 0};
  },
  componentDidMount: function() {
    this.setInterval(this.tick, 1000); // 调用 mixin 的方法
  },
  tick: function() {
    this.setState({seconds: this.state.seconds + 1});
  },
  render: function() {
    return (
      <p>
        React has been running for {this.state.seconds} seconds.
      </p>
    );
  }
});

React.render(
  <TickTock />,
  document.getElementById('example')
);

关于 mixin 值得一提的优点是,如果一个组件使用了多个 mixin,并且有多个 mixin 定义了同样的生命周期方法(如:多个 mixin 都需要在组件销毁时做资源清理操作),所有这些生命周期方法都保证会被执行到。方法执行顺序是:首先按 mixin 引入顺序执行 mixin 里方法,最后执行组件内定义的方法。

 

 

 

 

 

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

 

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

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

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

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

 

1.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

 

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

 

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

2.如何在子组件中改变父组件的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被修改了

3.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

 

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

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

posted @ 2018-01-02 16:24  铭绘  阅读(2278)  评论(1编辑  收藏  举报