react

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

理解JSX语法

JSX语法注意点:

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

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

例如: const jsx =

Hello Jsx

经过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 

hello,{this.props.name}

; } }

或者:
2.functional component

function Welcome(props) {
    return 

hello,{props.name}

}

组件中的数据源

  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 (
            

hello, {this.props.name}

{this.state.date.toString()}

) } } 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

事件绑定

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

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

  • 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函数的
      
) } testClick () { this.setState( (prevState) => { //setState()也能传函数作为参数,没什么好说的 return { isOn: !prevState.isOn } }) } } ReactDom.render(,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 (
    
{/*上面已经说过,绑定事件只能写函数,不能写函数的运行结果,所以如果写了preventClick()就会报错*/} click me
) } ReactDom.render(,rootDom)

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

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

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

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

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

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

先来看个例子:

class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      
) } 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 (
      
) } 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 (
      
) } 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 (
      
) } testClick() { console.log(this) } }

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

值得一提的是:

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

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

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

就像这样,自定义参数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 (
      
{/*ref callback*/} this.testRef = input} />
) } 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 (
       this.classCom = testComponent}>
    )
  }
  componentDidMount () {
    console.log(this.classCom.state)
  }
}

class TestComponent extends React.Component {
  constructor() {
    super()
    this.state = {
      name: 'children Component'
    }
  }
  render() {
    return (
      
) } }

这里的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()来进行操作

你可能感兴趣的:(react)