react

webpack --help -p :
shortcut for --optimize-minimize --define process.env.NODE_ENV="production"

理解JSX语法

JSX语法注意点:

  • 是 XML 不是 HTML,所以所有的标签都要闭合,如 <img /> 或 <div />。
  • 每段 JSX 里的 XML 只能有一个根元素,不然就报错。
  • XML 里面可以用 {} 混入 任何 JS 代码

JSX语法的理解:分为tagname,attributes,children三项来理解

例如: const jsx = <div>Hello Jsx</div>
经过babel之后为:

var jsx = React.createElement(
  "div",
  null,
  "Hello Jsx"
);

这样可以理解为:

{
  tagname: "div",
  attributes: null,
  children: "Hello Jsx"
}

组件的tagname就不是标准的html标签了,如果有props,那么attributes也不是null了,对于children来说,如果内部还有嵌套标签,那么依次做一个递归。

组件

react声明组件时,第一个字母必须大写

两种写法:

  1. class component
class Welcome extends React.Component {
    render () {
        return <h1>hello,{this.props.name}</h1>;
    }
}

或者:
2.functional component

function Welcome(props) {
    return <h1>hello,{props.name}</h1>
}

组件中的数据源

  1. props (props是父组件到子组件的,props should be pure,即不予许直接修改props)

  2. state (state是自身维护的数据状态,但也只可以通过setState修改)

组件中不可以改变props的值,state是组件中可以改变的东西

但是要使用this.setState()方法才能改变state的值

关于setState() , 参考

要理解2点:

  • setState不会立刻改变React组件中state的值
  • 函数式的setState用法(即setState()方法可以接受一个函数作为参数)
import React from 'react';
class Welcome extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            date: new Date()
        }
        setInterval(() => {
            this.setState({
                date: new Date()
            })
        })
    }
    render () {
        return (
            <div>
                <h1>hello, {this.props.name}</h1>
                <h2>{this.state.date.toString()}</h2>
            </div>
        )
    }
}

export default Welcome;


生命周期

声明周期英文文档

React 的生命周期包括三个阶段:mount(挂载)、update(更新)和 unmount(移除)

mount

mount 就是第一次让组件出现在页面中的过程。这个过程的关键就是 render 方法。React 会将 render 的返回值(一般是虚拟 DOM,也可以是 DOM 或者 null)插入到页面中。

这个过程会暴露几个钩子(hook)方便你往里面加代码:

  • constructor() 初始化props 和 state
  • componentWillMount() 准备插入render中return的内容
  • render() 开始插入
  • componentDidMount() 插入之后想进行的操作

update

mount 之后,如果数据有任何变动,就会来到 update 过程,这个过程有 5 个钩子:

  • 1.componentWillReceiveProps(nextProps) - 我要读取 props 啦!
    1. shouldComponentUpdate(nextProps, nextState) - 请问要不要更新组件?true / false
    1. componentWillUpdate() - 我要更新组件啦!
    1. render() - 更新!
    1. componentDidUpdate() - 更新完毕啦!

unmount

当一个组件将要从页面中移除时,会进入 unmount 过程,这个过程就一个钩子:

  • componentWillUnmount() - 我要死啦!

你可以在这个组件死之前做一些清理工作。

一般在下列钩子中应用setState():

  • componentWillMount
  • componentDidMount
  • componentWillReceiveProps

事件绑定

  • 首先明确一个概念,就是在<div onClick=fn></div>这个DOM绑定中,onClick后面的fn是一个函数,而不是一个函数执行的结果,所以不能写成<div onClick=fn()></div>。(重点!)

明确了函数绑定时,写的是一个函数而不是函数运行的结果之后,继续明确第二个概念:

  • bind(),对于一个函数fn来说,fn.bind(window)同样是一个函数,只不过运行的Context指定为了window。fn.bind(window)本身并没有执行(所以仍然是一个函数而不是函数运行的结果!)

明确了上述2点之后,再来看react中的事件绑定。

先来看一个App组件:

import React from 'react'
import ReactDom from 'react-dom'
import './index.css'

const rootDom = document.querySelector('#root')

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isOn: true
    }
  }
  render () {
    return (
      //这里点击的时候的this已经不是当前组件了,所以要bind为当前组件,不然是没有testClick函数的
      <div onClick={this.testClick.bind(this)}> 
        <button>{this.state.isOn ? 'ON' : 'OFF' }</button>
      </div>
    )
  }
  testClick () {
    this.setState( (prevState) => { //setState()也能传函数作为参数,没什么好说的
      return {
        isOn: !prevState.isOn
      }
    })
  }
}
ReactDom.render(<App></App>,rootDom)

这么一看就能理解react中的事件绑定了。

  • 还有一个小问题,对于标签默认行为的阻止,不像Vue一样有.prevent这种修饰符,还是要自己写的。
    来看个例子:
import React from 'react'
import ReactDom from 'react-dom'
import './index.css'

const rootDom = document.querySelector('#root')

let App = () => {
  function preventClick (e) { // 注意这里的e,是react自己传过来的  本身没做任何操作
    e.preventDefault() //
    console.log('default click has been prevent')
  }
  return (
    <div>
      {/*上面已经说过,绑定事件只能写函数,不能写函数的运行结果,所以如果写了preventClick()就会报错*/}
      <a href="http://www.baidu.com" onClick={preventClick}>click me</a>
    </div>
  )
}
ReactDom.render(<App></App>,rootDom)

对于上述问题 又引申出了一个小问题,如果我想在阻止a标签的同时,事件处理函数还想接收一些其他的参数进行处理怎么写呢。

对于React,上面例子的e.preventDefault()中的e是React自己传过来的。我们如果既想写e,还想有自己规定的一些参数的话,要这么写:

let App = () => {
  function preventClick (str,e) { // 如果有其他自定义的参数,那么e永远是最后一项,而且同样也是React自己传过来的。
    e.preventDefault() //
    console.log(str)
  }
  return (
    <div>
      {/*上面已经说过,绑定事件只能写函数,不能写函数的运行结果,所以如果写了preventClick()就会报错*/}
      <a href="http://www.baidu.com" onClick={preventClick.bind(this,'testString')}>click me</a>
      {/*这里的preventClick.bind(this,'testString')同样也是一个函数,并不是函数运行结果*/}
    </div>
  )
}

上面的例子中,我们想在preventClick()中传一些自己用的参数,那么在点击的时候,就要bind到组件本身上,然后再传入想要的参数,注意e是不用我们自己写到参数中去的,回到函数本身的参数上,自定义的参数在前,e永远是最后一项。

摆脱React中操蛋的bind(this)的方法

上面的事件绑定中已经提到了bind,因为事件触发时的this已经不是当前组件本身了,所以我们要将处理函数的this重新设置为当前组件,所以有了随便一个函数后面都要bind(this)的情况。

先来看个例子:

class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick.bind(this)}>click me</button>
      </div>
    )
  }
  testClick() { // 普通函数在被调用的时候,就有了this,所以要加bind()到App组件上
    console.log(this) 
  }
}

上述例子中,testClick是App组件上的函数,点击时的this已经不是App组件了,所以不能正确调用。要将其写为this.testClick.bind(this)而不是this.testClick

为了摆脱上述这种烦人的写法,我们可以有如下几种方法:

  1. constructor中将当前函数重新赋值。
class App extends React.Component {
  constructor(props) {
    super(props)
    this.testClick = this.testClick.bind(this) //这的this是App组件,直接将testClick重新赋值一下
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick}>click me</button>
      </div>
    )
  }
  testClick() { // 普通函数在被调用的时候,就有了this,所以要加bind()到App组件上
    console.log(this)
  }
}

我们在constructor中给testClick重新赋了一次值,testClick变为了一个运行上下文为App组件的函数,下面直接onClick={this.testClick}即可。

  1. public class fields syntax(不知道怎么翻译,就是利用箭头函数没有this的特性)
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick}>click me</button>
      </div>
    )
  }
  testClick = () => { //箭头函数没有本身的this 所以定义的时候this已经确定为App组件了,所以不用bind
    console.log(this)
  }
}

我们利用箭头函数本身没有this的特性,在App中定义testClick的时候,testClick的this就已经确定为当前上下文App组件了。所以后面直接调用onClick={this.testClick}即可。

  1. 在回调中使用箭头函数
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={(e) => {return this.testClick(e)}}>click me</button>
      </div>
    )
  }
  testClick() { 
    console.log(this)
  }
}

每次渲染组件时,都会生成一个全新的回调。但在有时候将其作为prop传给子组件时,会引发一次额外的渲染。所以,建议用constructor 和public class fields syntax两种方式避免bind(this)这种写法。

值得一提的是:

上面说的想给事件处理函数传递另外自定义的参数时,绑定事件的时候是不用写e的,因为react是自动帮你把e作为最后一个参数传递的

<button onClick={this.testClick.bind(this, id)}>click me</button>

就像上述例子一样,我们想传一个额外的参数id,在id后面是不需要写e的,this.testClick.bind(this, id,e)这种是不用写的。

但是! 在我们提到的第3种方法,回调中使用函数的情况下,是需要开发者自己写上e的!

<button onClick={(e) => this.testClick(id, e)}>click me</button>

就像这样,自定义参数id的后面还要写上e。

详情参考文档:With an arrow function, we have to pass it explicitly, but with bind any further arguments are automatically forwarded.

文档地址

React中的ref

想直接修改原生DOM或者是组件的时候,可以使用ref。

  1. 原生DOM上
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    return (
      <div>
        {/*ref callback*/}
        <input type="text" ref={(input) => this.testRef = input} />
        <input type="button" onClick={this.focus.bind(this)} value="Click" />
      </div>
    )
  }
  focus() {
    this.testRef.focus();
  }
}

上述例子就是一个原生DOM上使用ref的例子,react在原生的input加载完成后,通过一个回调函数,(input) => this.testRef = input,这里的回调参数input就是底层的DOM,接受DOM作为参数,存到testRef中。引用的时候,直接就引用到了原生的DOM

  1. ref ====> class Component
// ref ===> class Component
class App extends React.Component {
  render() {
    return (
      <TestComponent ref={(testComponent) => this.classCom = testComponent}></TestComponent>
    )
  }
  componentDidMount () {
    console.log(this.classCom.state)
  }
}

class TestComponent extends React.Component {
  constructor() {
    super()
    this.state = {
      name: 'children Component'
    }
  }
  render() {
    return (
      <div>
        <input type="text"/>
      </div>
    )
  }
}

这里的ref回调函数中,(testComponent) => this.classCom = testComponent的参数为已经加载的 React 实例,我们可以在父组件中通过this.classCom访问到它。

  1. ref ====> functional Component

你不能在函数式组件上使用 ref 属性,因为它们没有实例,但是可以对其内部的原生DOM使用ref,参见第一条。

React中突变数据的处理

经常会遇到设置了state之后,进行setState()了之后,页面没有进行渲染的情况。(尤其是使用PureComponent的时候)。

这种情况经常是由于操作习惯不好造成了原数据结构的突变

  • 先来解释什么是原数据突变
    let arr = [1]
    arr.push(2)
    console.log(arr) //[1, 2]

我们有一个数组,进行了push操作之后,原先的数组的值改变了,由[1]变为了[1,2]

看下一个例子:

    let arr2 = [1]
    let arr3 = arr2.concat([2])
    console.log(arr2) //[1]
    console.log(arr3) //[1, 2]

同样是一个数组,进行了concat操作之后,原先的arr2的值并没有发生改变,我们把concat之后的结果赋给了一个新变量arr3来储存。

  • React中setState()时我们该怎么写数据

上面我们提到了push操作会改变原数据,这在React中是不应该的。

setState时,我们应该return的是一个新对象,而不是原对象。所以我们要避免那些会修改原数据的操作,例如push

比如说,我们在一个组件中通过push()修改了数组,然后有一个PureComponent子组件把这个数组作为props渲染页面,这时候虽然我们在父组件中修改了数组的值,但是setState()return的时候,原数据结构已经变了,这时候虽然本身的state已经改变了,但是传到子组件的props并没有改变。

为了避免这种情况发生,我们一般可以采用concat等不会引起原数据突变的操作。

如果非要用push的话,可以通过Object.assgin()或者...spread语法来进行一份数据的拷贝,在进行操作。

比如说上述的arr,我们可以这么操作:

    this.state = {
      arr: [1]
    };
//进行setState()
    this.setState( (prevState) => {
      return {
        arr: [...prevState.arr,2]
      }
    })

如果目标数据是对象的话,可以通过Object.assgin()来进行操作

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容

  • 说在前面 关于 react 的总结过去半年就一直碎碎念着要搞起来,各(wo)种(tai)原(lan)因(le)。心...
    陈嘻嘻啊阅读 6,864评论 7 41
  • 自己最近的项目是基于react的,于是读了一遍react的文档,做了一些记录(除了REFERENCE部分还没开始读...
    潘逸飞阅读 3,373评论 1 10
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,823评论 0 24
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,061评论 2 35
  • 使用 create-react-app 快速构建 React 开发环境 项目的目录结构如下: React JSX ...
    majun00阅读 510评论 0 0