React入門

如下內容,當具有ES6,JS語法,node環境,前端組件基礎概念,寫過java代碼,包你3天上手React項目,下面開始...css

demo地址

https://gitee.com/tonysb/lear...
項目結構以下,Button的方式值得借鑑
clipboard.pnghtml

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來接住屬性
子->父 經過在子中調用父傳遞的方法來完成java

具體使用場景: 一個頁面右上角掛載一個三級聯動選項卡(三級數據從接口獲取),main做爲父組件主頁面,select做爲子組件三級選項卡頁面node

  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高階函數,綁定的是函數,不能讓函數立刻執行
不帶參數綁定,後面不能加(), 若是加,那函數會立刻執行,而不是事件發生時候回調執行
帶參數綁定,須要在一個匿名函數中寫綁定函數react

// 合成事件綁定,不帶參數
<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變量中git

state和props和render

state: 組件本身內部維護數據,每次更新只須要關係須要更改的數據
1, set數據不依賴以前的數據,直接調用setStateajax

this.setState({ 
               foo: bar 
            })

2, set數據依賴以前數據,必定要使用帶參數調用setStatechrome

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都會被執行redux

ajax接參數

  1. 子組件是UI組件,接完父組件數據直接使用,可直接在子組件props中直接使用父組件ajax請求獲取到的數據
  2. 子組件須要將父組件ajax數據賦值到本身的state中,只能在componentWillReceiveProps生命週期中處理,不能在constructor中處理,緣由: 父組件ajax請求最起碼第二次render父組件,而子組件中constructor只會執行一次

上述方式處理方式比較麻煩,推薦使用方式3
3, 父組件負責提供數據和處理處理的函數,子組件只負責獲取數據,渲染頁面,子組件事件交互,也是經過props來調用父組件中的函數(經常使用)

生命週期(重要)

圖片描述
如下是使用場景和注意點
組件掛載階段(只會執行一次,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的值
相關文章
相關標籤/搜索