React組件和組件數據通訊

前言

文章涉及的內容可能不全面,但量不少,須要慢慢看。我花了很長的時間整理,用心分享心得,但願對你們有所幫助。可是不免會有打字的錯誤或理解的錯誤點,但願發現的能夠郵箱告訴我1163675970@qq.com,我會及時的進行修改,只但願對你有所幫助,謝謝。css

1、寫一個時鐘

  • 用 react 寫一個每秒均可以更新一次的時鐘
import React from 'react'
import ReactDOM from 'react-dom'


function tick() {
    let ele = <h1>{ new Date().toLocaleTimeString() }</h1>

    // Objects are not valid as a React child (found: Sun Aug 04 2019 20:34:51 GMT+0800 (中國標準時間)). If you meant to render a collection of children, use an array instead.
    // new Date() 是一個對象數據類型的值,React 元素不接收對象做爲其子元素
    ReactDOM.render(ele, document.querySelector('#root'))
}

tick()

setInterval(tick, 1000) // 若是不包在一個函數中,時鐘是不會每秒更新一次
複製代碼

可是 React 和 Vue 相同都是數據驅動的,可是這個時候和數據驅動沒啥關係,每隔1秒鐘從新建立一個 ele,而後再渲染到頁面中,視圖才發生變化;爲了使用數據驅動,咱們須要使用 React 的組件html

2、React 的組件

在 React 組件中,jsx 元素(也稱 react 元素)是組件的基本組成單位 在 react 中定義組件有兩種方式:react

  1. 函數(function)定義組件
  2. 類(class)定義組件
  • 定義組件的要求:
    1. 組件的名字首字母必須大寫,爲了在寫 jsx 時區分原生 html 標籤
    1. 組件定義後,就能夠當作一個標籤在 jsx 語法中使用
    1. 若是使用函數定義組件必須返回一個 jsx 元素

2.1 React 的函數組件

react 使用函數定義組件,就是聲明一個函數;算法

  • 函數接收一個 props 參數;props 是對象,是在渲染或者父組件經過 prop(屬性) 傳遞過來的數據;
  • 函數返回一個 jsx 元素,在組件中須要的數據能夠經過 props 傳入;
// 1. 函數定義組件
function Welcome(props) {
    // props 是一個對象,是使用組件時,寫在組件行內的屬性和屬性值組成的;
    console.log(data)
    return (<div>
        <p>{props.data.name}; {props.data.age}</p>
        <p>{props.x}</p>
    </div>)
}

ReactDOM.render(<Welcome data={{name: 'mabin', age: 18}} x='hahah' />, document.querySelector('#root'));
複製代碼
  • ReactDOM.render() 會根據第一個參數的類型不一樣執行不一樣的操做;
    1. 若是是組件,當 render 執行時,首先會把當前組件的行內屬性進行打包封裝,把其封裝成一個對象,把這個對象傳給組件函數
    1. 執行組件函數,獲取對應的虛擬 DOM 對象
    1. 把虛擬 DOM 轉成真實 DOM 對象,而且插入到真實的 DOM 中

2.2 React 的 class 組件

經過 class 定義一個組件bootstrap

  1. 經過 class 來定義一個組件,須要繼承 React 上的 Component 這個類
  2. 在定義組件上的原型上必須有一個 render 函數,且 render 函數須要返回一個頂級的 jsx 元素

-看🌰數組

class Header extends Component {
    constructor () {
        super()
    }

    render () {
        // 在 render 函數中經過 this.props 訪問 props
        return (<div>
            {this.props.content}
        </div>)
    }
}

class Hello extends Component {
    constructor (props) {
        super()
        // 注意在構造函數中不能訪問 this.props ,props 會做爲形參傳入
    }

    render () {
        return (
            <div>
                <Header content="如今是北京時間:" />
                <p>{this.props.data.toLocaleString()}</p>
            </div>
        )
    }
}

// 使用這個組件
ReactDOM.render(<Hello data={new Date()} />, document.getElementById('root'));
複製代碼
  • ReactDOM.render() 渲染 class 聲明的組件過程:
    1. 找到組件對應的類,而後 new 一下這個類,得到這個類的一個實例
    1. 經過實例找到當前類原型上的 render 函數,讓 render 執行接收其返回的虛擬 DOM
    1. 將上一步的虛擬 DOM 轉換成成真實 DOM ,插入到頁面中

2.3 class 和 function 定義的組件有什麼不一樣

React 也是數據驅動的,當數據發生變化時,視圖就會自動發生變化(視圖是數據的映射)。組件中的數據有兩個來源:props 和 state,其中 props 就是組件被使用時接收的行內屬性,是從外部傳入的數據,而 state 是組件的私有數據,組件定義時就須要建立;bash

  1. class 定義的組件中有 this,state,生命週期的鉤子,而 function 聲明的組件只有 props;

3、數據映射視圖

3.1 屬性(props)映射視圖

屬性(prop)也是組件的數據,而視圖是數據的映射,當數據發生變化,組件會自動從新渲染dom

-看🌰函數

function Welcome(props) {
    return <div>{props.time.toLocaleString()}</div>
}


setInterval(() => {
    // 每隔一秒鐘 new Date的值會發生變化,即 Welcome 的 time prop 屬性發生了變化,而視圖自動變化
    let now = new Date()
    ReactDOM.render(<Welcome time={now} />, document.querySelector('#root'))
}, 1000)
複製代碼

-看🌰ui

把數據經過屬性傳遞給組件

function User(props) {
    console.log(props)
    let { name, age } = props;
    return <div>
        <p>{name}</p>
        <p>{age}</p>
    </div>
}

let data = {
    name: 'mabin',
    age: 18
}

// ReactDOM.render(<User name={data.name} age={data.age} />, document.getElementById('root'))

ReactDOM.render(<User {...data}/>, document.getElementById('root')) // 可使用展開運算符把一個對象傳給組件的props,等效於上面的寫法
複製代碼

3.2 狀態(state) 映射視圖

react 組件的數據有兩個來源:props 和 state 屬性(props):是父組件傳遞過來的 狀態(state): 是組件本身管控的狀態,狀態是組件私有的數據

3.2.1 使用 state

  • 在 React 中若是使用 state 必須使用 class 建立組件;
  • 在 constructor 中初始化一個狀態;經過 this.state 賦值一個對象的形式初始化;
  • state 中的數據不能夠直接修改,若是要更新數據,須要調用 setState 方法 ,setState 方法會進行合併 setState有兩種寫法 一種是對象一種是函數,若是下一個狀態依賴上一個狀態,咱們須要使用函數的形式
    • 函數: this.setState((prevState) => {})
    • 對象: this.setState({num: 5})
  • state 發生改變後觸發 render 函數執行更新 DOM

3.2.2 在 react 中綁定事件

  • react 綁定事件時,須要使用駝峯命名法的事件名 onClick = { 事件處理函數 }
  • 在定義事件函數時,通常把事件函數聲明在原型上,而綁定事件時,經過 this.add 訪問這個事件函數
  • 示例:

咱們來寫一個計數器感覺一下 React 的數據驅動

class Count extends Component {
  constructor () {
    super()

    // 在 constructor 中初始化一個狀態;經過this.state 賦值一個對象的形式初始化;
    // 只有用類聲明的組件纔有 state
    this.state = {
      num: 1,
      x: 2
    }
    // this.add = this.add.bind(this)
  }



  add = () => {
    // 在 react 中若是要修改 狀態只能經過 this.setState() 方法修改
    // setState 方法會進行合併 setState 有兩種寫法 一種是對象一種是函數

    // 1. setState 能夠接受一個回調,回調須要 return 一個新的 state 對象,新的對象中只需包含要修改的 屬性便可,例如這裏咱們要修改 num,return 的對象只須要包含num不用包含 x,react 會自動合併
    // 若是下一個狀態依賴上一個狀態,咱們須要使用函數的形式
    /*this.setState((prevState) => {
      console.log(prevState); // prevState 以前的狀態對象
      return {
        num: prevState.num + 1
      }
    })*/

    // 2. setState 還能夠接受一個對象,對象中須要包含要更新的 state 屬性;
    this.setState({
      num: this.state.num + 1
    })

    // 咱們發現,咱們更新數據後,頁面中使用 num 的地方的值也自動跟着改了;
    // react 一樣是數據驅動的,當咱們調用 setState 修改 state 時,react 會從新調用 render 函數,獲得虛擬DOM 而後調用 DOM-diff 算法,把修改的那一部分從新渲染;
  }

  render () {
    // react 綁定事件時,須要使用駝峯命名法的事件名 onClick = { 事件處理函數 }

    // 在定義事件函數時,通常把事件函數聲明在原型上,而綁定事件時,經過 this.add 訪問這個事件函數

    return (<div>
      <p>NUM: {this.state.num} </p>
      <p>X: {this.state.x} </p>
      <button onClick={this.add}>給num加1</button>
    </div>)
  }
}

ReactDOM.render(<Count />, document.getElementById('root'))
複製代碼

4、屬性(props) 校驗

和 Vue 的 props 同樣,React 的 props 一樣支持校驗;React 的 props 校驗須要三方的庫 prop-types

4.1 安裝 prop-types

yarn add prop-types --save
複製代碼

4.2 使用

使用 類型校驗須要 在 class 建立組件時建立靜態屬性 propTypes,值是一個對象,對象的屬性是須要校驗的 屬性,值對應的是校驗規則;

  • 類型校驗看🌰
static propTypes = {
    name: PropType.string.isRequired, // 要求 name 是字符串類型 isRequired 表示必傳
    age: PropType.number.isRequired // 要求 age 是數字類型,isRequired 表示必傳
}
複製代碼
  • 此外,還能夠給 prop 設置默認值,一樣是經過類的靜態屬性設置,在建立組件時須要配置 defaultProps 靜態屬性;該屬性的值是一個對象,該對象中屬性是要設置默認值的 prop,值是 prop 的默認值
static defaultProps = {
    name: '珠峯',
    age: 10
 }
複製代碼
  • 完整🌰
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import PropType from 'prop-types'
// React 的props 一樣能夠校驗,可是須要一個第三方的庫 prop-types

class User extends Component {
    constructor (props) {
        super()

        console.log(props) // 對象,把行內屬性封裝到一個對象中

        // props.name = 123 // 若是想對 props 進行修改,能夠在 constructor 中進行修改
    }

    static propTypes = {
        name: PropType.string.isRequired, // 要求 name 是字符串類型 isRequired 表示必傳
        age: PropType.number.isRequired // 要求 age 是數字類型,isRequired 表示必傳
    }

    // 給props 設置默認值
    static defaultProps = {
      name: '京東',
    age: 19
  }

    render () {
        return (<div>
            <p>{ this.props.name }</p>
            <p>{ this.props.age }</p>
        </div>)
    }
}

let obj = {
    name: '張三',
    age: 18
};

ReactDOM.render(<User {...obj} />, document.querySelector('#root'));
複製代碼

5、父子組件通訊

5.1 父傳子

在 React 中,父組件把數據傳遞給子組件,仍然是經過 props 的方式傳遞;

-看🌰

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class Header extends Component {
  render () {
    return (<h1>
      <p>{this.props.data}</p>
    </h1>)
  }
}

// 此時的 Panel 是父組件而 Header 是子組件,父子組件通訊時父傳子,仍然是經過 props 傳遞的
class Panel extends Component {
  render () {
    return (<div className="container">
      <p>{this.props.news}</p>
      <Header data={this.props.min} />
    </div>)
  }
}

let data = {
  news: '快下課了',
  min: '拖幾分鐘'
}

ReactDOM.render(<Panel {...data} />, document.getElementById('root'))
複製代碼

5.2 子傳父

在 React 中子組件修改父組件的方式和 Vue 不一樣;子組件若是想修改父組件的數據,父組件在使用子組件的時候,經過 props 傳給子組件一個能夠修改父組件的方法,當子組件須要修改父組件的數據時,經過 this.props 找到這個方法執行對應的方法

-看🌰

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

import 'bootstrap/dist/css/bootstrap.css'

class Panel extends Component {
  static defaultProps = {
    a: 1
  }
  constructor () {
    super()

    this.state = {
      color: 'success'
    }
  }

  changeColor = (color) => {
    this.setState({
      color
    })
  }

  render () {
    return (<div className="container">
      <div className={`panel panel-${this.state.color}`}>
        <div className="panel-heading">
          {this.props.head}
        </div>
        <div className="panel-body">
          {this.props.body}
        </div>
        {/*經過 modifyColor 這個 props 把 Panel 組件的 changeColor 方法傳遞給 Footer 組件*/}
        <Footer type={this.state.color}
                modifyColor={this.changeColor} />
      </div>
    </div>)
  }
}

class Footer extends Component {
  change = () => {
    this.props.modifyColor('danger')
  }
  render () {

    return (<div className="panel-footer">
      <button className={`btn btn-${this.props.type}`} onClick={this.change}>變色</button>
    </div>)
  }
}

ReactDOM.render(<Panel head="頭信息" body="信息主體"/>, document.getElementById('root'))

// React 一樣是單向數據流,即數據只能經過只能從父組件流向子組件
// 因此子組件若是想修改父組件的數據,父組件在使用子組件的時候,經過props傳給子組件一個能夠修改父組件的方法,當子組件須要修改父組件的數據時,經過this.props 找到這個方法執行對應的方法就能夠了
複製代碼
相關文章
相關標籤/搜索