React入门

以下内容,当具备ES6,JS语法,node环境,前端组件基础概念,写过java代码,包你3天上手React项目,下面开始…

demo地址

https://gitee.com/tonysb/lear…
项目结构如下,Button的方式值得借鉴
《React入门》

react介绍

  1. react: 一个js框架,让开发者可以在js中写html代码,也就是jsx语法,称为虚拟dom(类似一个js对象)
  2. react-dom: 挂载节点,将jsx写的虚拟dom变成真的dom
  3. render: 每次都是新旧虚拟dom之间进行比较,之后才会生成真实dom

创建react项目

命令行使用 npx create-react-app my-app-name 即可创建项目

react中组件(子父之间传值)

A: 父组件 B: 子组件
在A.js文件中使用<B name={name} getName={this.getName}> 其中name为A文件中一个变量,getName为A文件中一个方法
在B.js文件中,可直接使用this.props.namethis.props.getNameconst { name, getName } = this.props来得到A中变量,或运行A中方法,最后一种最常用,这种方式是ES6中新增的解构赋值.
父->子 通过子标签上加上属性的方式,直接传递,在子重使用this.props来接住属性
子->父 通过在子中调用父传递的方法来完成

具体使用场景: 一个页面右上角挂载一个三级联动选项卡(三级数据从接口获取),main作为父组件主页面,select作为子组件三级选项卡页面

  1. main页面负责ajax请求,拿总数据,为子组件准备好所有即插即用数据和方法,在使用select标签的时候,全部传递给子
  2. select页面,在this.props中负责解构所有数据和方法,直接使用,无需关心逻辑实现
  3. main叫逻辑组件(聪明组件), select叫UI组件(笨蛋组件)

JSX语法

  1. render中return中的代码都是JSX,和html代码相似.
  2. 在js中直接写html语法,也可以使用自定义标签,比如可以写自己组件,App组件可以写成 <App />,首字母必须大写开头,JSX标签中,大写开头,基本都是组件,小写开头基本是html标签.
  3. render中用JSX语法写html代码,必须在最外层包括一个div,不然编译会报错,在16版本中,如果使用Fragment来表示占位符,在html中显示的时候,可去掉组件最外层的那个div,demo如下
<React.Fragment>
  // 你的jsx代码
</React.Fragment>
  1. 使用js表达式/js变量需要使用{}把表达式包裹起来,demo如下
// ES6 ``,${}加三目运算符可处理复杂的css名字
<div className={`classA classB ${this.state.isSelect ? 'selected' : ''}`} />
// for循环遍历请使用map方法,sortTableRows是state中一个变量
 {sortTableRows && sortTableRows.map(item => (
      <div key={item.id} className="table-row">
        <span>{item.title}</span>
        <span>{item.author}</span>
        <span>{item.comments}</span>
        <span>{item.points}</span>
        <span>
          <CancelButton onClick={() => onMiss(item.id)}>miss</CancelButton>
        </span>
      </div>
))}
  1. html标签中进行事件绑定,事件名称首字母必须大写,比如 onChange中C就是大写的
  2. 在html样式中,使用className来代替class
  3. 使用dangerouslySetInnerHTML={{__html: item}},可在提交数据的时候,对数据中html标签进行转义处理
  4. <label for=”insertArea”>输入内容</label> for需要换成htmlfor

事件绑定

jsx合成事件绑定,js高阶函数,绑定的是函数,不能让函数马上执行
不带参数绑定,后面不能加(), 如果加,那函数会马上执行,而不是事件发生时候回调执行
带参数绑定,需要在一个匿名函数中写绑定函数

// 合成事件绑定,不带参数
<li onClick={this.props.delItem}>{item}</li>
// 合成事件绑定,带参数
<li onClick={(index) => this.props.delItem(index)}>{item}</li>

表单交互(input)

1, state中设置searchTerm变量,用来填充input中value值
2, 绑定input中onChange事件,获取event对象中value值,将该值set到searchTerm变量中

state和props和render

state: 组件自己内部维护数据,每次更新只需要关系需要更改的数据
1, set数据不依赖之前的数据,直接调用setState

this.setState({ 
               foo: bar 
            })

2, set数据依赖之前数据,一定要使用带参数调用setState

this.setState((prevState, props) => (
    // 你的代码  
))

// bad
// setState是异步,代码中多个set,有可能fooCount和barCount已经被改变
const { fooCount } = this.state
const { barCount } = this.props
this.setState({ count: fooCount + barCount })
// good
this.setState((prevState, props) => (
    count: prevState.fooCount + props.barCount
))

props: 父->子,所有传递过来的数据都附加在props中,子在props中可以拿到所有父传递过来的数据
render: 只要state进行set操作,render一定会执行,包括父state进行set之后,父和子的render都会被执行

ajax接参数

  1. 子组件是UI组件,接完父组件数据直接使用,可直接在子组件props中直接使用父组件ajax请求获取到的数据
  2. 子组件需要将父组件ajax数据赋值到自己的state中,只能在componentWillReceiveProps生命周期中处理,不能在constructor中处理,原因: 父组件ajax请求最起码第二次render父组件,而子组件中constructor只会执行一次

上述方式处理方式比较麻烦,推荐使用方式3
3, 父组件负责提供数据和处理处理的函数,子组件只负责获取数据,渲染页面,子组件事件交互,也是通过props来调用父组件中的函数(常用)

生命周期(重要)

《React入门》
以下是使用场景和注意点
组件挂载阶段(只会执行一次,render除外)
constructor(): 设置state,获取父组件props,并初始化自己的state,并绑定函数中this指向
componentWillMount(): 不能获取页面dom元素,目前还没用过
render():渲染页面,不做运算,到这里为止,所有数据都应该是经过处理可直接使用的数据
componentDidMount(): 可获取dom元素,ajax请求放这里,调用setState,设置dom元素监听,绘制canvas
组件更新阶段(state或props发生变化)
componentWillReceiveProps(nextProps): nextProps为更新新属性,可进行新旧属性对比,也可以根据新数据来计算和设置自己的state
shouldComponentUpdate(nextProps, nextState): 返回布尔值,可做渲染优化,根据场景决定是否渲染该组件,不要调用setState
componentDidUpdate(prevProps, prevState): 操作 DOM 或者执行更多异步请求的机会
componentWillUnmount: 取消网络请求,删除监听,一些收尾工作,不要调用setState

react中this指向

  1. 自定义函数的时候,写自定义函数,需要在constructor中进行函数this指向的绑定
  2. 在constructor中进行this指向绑定,然后写正常函数
// 方式1
  getTodoItem = () => {
    // 你的逻辑
  }
// 方式2
 constructor(props) {
    super(props)
    this.inputOnChange = this.inputOnChange.bind(this)
  }
 inputOnChange() {
    // 你的逻辑
  }

组件类型

ES6类组件: 有this,props,有state,有生命周期,根据设计图,一般当作父组件使用,从接口拿数据,计算成品数据,编写事件函数来处理数据
无状态组件(纯函数): 接收输入(props),输出jsx组件实例,没有this,没有生命周期,没有state,可根据props中的数据来计算符合自己要求的数据(组件中直接调用方法),
props中可以传递数据,也可以传递函数,一般当做子组件使用,demo如下

import React from 'react'

/*查询表单组件
* Search在使用的时候,标签中间就是children
* <Search value={searchTerm} onSubmit={this.onSubmit} onChange={this.onChange}>查询</Search>  children的值就是查询
*/
const Search = ({value, onChange, onSubmit, children}) => {
  return (
    <div>
      <form onSubmit={onSubmit}>
        <input type='text' value={value} onChange={onChange}/>
        <button type='submit'>{children}</button>
      </form>
    </div>
  )
}

export default Search
import React from 'react'
import {Slider} from "antd"
import moment from "moment"
import './style.less'

/*
* 时间轴无状态组件
* timeData: 时间数组列表
* timeChange: 时间轴change事件的回调
* worktime: 当前选中时间
* */
const TimeSlider = ({timeData, timeChange, worktime}) => {

  /*
  * 获取时间轴当前选中值
  * */
  const getDefaultValue = () => {
    const { id } = times.find(item => {
      return item.dateTime === worktime
    })
    return id
  }

  /*
  * 获取时间轴的总长度
  * */
  const getMax = () => {
    return timeData.length
  }

  /*
  * 获取时间轴显示的time数据
  * 格式:
  * {
  *  0: '2019-11-11'
  *  24: '2019-11-12'
  *  23: '2019-11-13'
  * }
  * */
  let times = []
  const getMarks = () => {
    let dates = []
    console.log('timeData len', timeData.length)
    timeData.map((item, index) => {
      const time = {id: index, dateTime: item}
      times.push(time)
      dates.push(moment(item).format('YYYY-MM-DD'))
    })
    const newDates = [...new Set(dates)]

    console.log('newDates', newDates)
    let marks = {}
    let datesAmount = []
    newDates.map(item => {
      const amount = getDateAmount(item, timeData)
      const temp = {date: item, amount: amount}
      datesAmount.push(temp)
    })

    for (let i = 0,length = datesAmount.length; i < length; i++) {
      const date =  {
        style: {
          color: '#fff',
          marginLeft: 0
        },
        label: moment(datesAmount[i].date).format('MM/DD'),
      }
      if (i === 0) {
        //const mark =  JSON.parse(`{"0":"${date}"}`)
        const mark = {0: date}
        Object.assign(marks, mark)
      } else {
        const index = i - 1
        const lastAmount = getLastDateAmount(index, datesAmount)
       // const mark =  JSON.parse(`{"${lastAmount}":"${date}"}`)
        const mark =  { [lastAmount]: date }
        Object.assign(marks, mark)
      }
    }
    return marks
  }

  /*
  * 根据 2019-11-11来获取 数据 ['2019-11-11 11:11:11'] 对应的小时数量
  * */
  const getDateAmount = (date, timeData) => {
    let amount = 0
    for (let i = 0,length = timeData.length; i < length; i++ ) {
      const item = timeData[i]
      if (item.includes(date)) {
        amount = amount + 1
      }
    }
    return amount
  }

  /*
  * 获取当前index往前推一个的所有小时数量
  * */
  const getLastDateAmount = (index, datesAmount) => {
    let sum = 0
    for (let i = 0; i <= index; i++) {
      sum = sum + datesAmount[i].amount
    }
    return sum
  }


  const formatter = (value) => {
    const time = times.find(item => {
      return item.id === value
    })
    return moment(time.dateTime).format('HH:mm')
  }

  const onTimeChange = (value) => {
    const time = times.find(item => {
      return item.id === value
    })
    timeChange(time.dateTime)
  }

  return (
    <div className='time-slider'>
      <Slider max={getMax()} marks={getMarks()} defaultValue={getDefaultValue()} value={getDefaultValue()} tipFormatter={formatter} onChange={onTimeChange}/>
    </div>
  )
}

export default TimeSlider

顶层API

React.createRef

作用: 在当前组件中,创建dom,获取dom

class createRefDemo extends Component {

    constructor(props) {
        super(props)
        this.myInput = React.createRef()
    }
    
     handleInputChange = () => {
        const value = this.myInput.current.value // 这里通过current可获取input的dom对象
    }
  
    render() {
      return (
          <input id="hello" className='input' type="text" value='hello' 
                 onChange={this.handleInputChange} ref={this.myInput}/>
      )
    }
}

React.forwardRef

参考链接: https://juejin.im/post/5c0dd4…
作用: 将refDom可以传递给子孙组件,永远传递下去,在HOC中常用

ReactDOM.createPortal

作用: 脱离父组件,将该组件挂载到dom的任何位置

// 将selectComp组件挂载到bodyDom下面
const selectComp = <Selectcj />
ReactDOM.createPortal(
              selectComp,
              document.querySelector('body'),
            )

React.createContext

Provider和Consumer

两款chrome插件

  1. React Developer Tools // F12,查看每个react组件中state和props的值
  2. Redux DevTools // F12,查看redux中store的值
    原文作者:wujiedong
    原文地址: https://segmentfault.com/a/1190000019058420
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞