React 基礎筆記

概覽

React 是一個聲明式,高效且靈活的用於構建用戶界面的 JavaScript庫。能夠將一些簡短、獨立的代碼片斷組合成複雜的UI界面,這些片斷被稱爲「組件」。javascript

React 大致包含下面這些概念:前端

  • 組件
  • JSX
  • Virtual DOM
  • Data Flow

組件

能夠將UI 拆分爲獨立且複用的代碼片斷,每部分均可獨立維護。
組件,從概念上相似於 JavaScript 函數。它接受任意的參數(即 「props」),並返回用於描述頁面展現內容的React 元素
自定義組件命名:必須以大寫字母開頭,React 會將以小寫字母開頭的組件視爲原生DOM標籤。vue

import React from 'react'; // React 的核心庫
import ReactDOM from 'react-dom'; // 提供與 DOM 相關的功能
class ShoppingList extends React.Component {
  render() {
    return (
      <div className="shopping-list">
        <h1>Shopping List for {this.props.name}</h1>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

ReactDOM.render(
  <ShoppingList name="Mark" />,
  document.getElementById('root')
);

React 應用都是構建在組件之上。ShoppingList就是一個React 組件類型,ReactDOM.render 函數會將組件方到頁面上的某個節點元素中。(render 返回了一個 React 元素 ,這是一種對渲染內容的輕量級描述。)
大多數 React應用只會調用一次 ReactDOM.render()java

其中props(是 properties 的簡寫) 是組件包含的兩個核心概念之一,另外一個是statereact

props

props接收一些其餘組件的參數(好比上方的 name ),來配置組件,全部 React 組件都必須像純函數同樣保護它們的 props 不被更改git

state

state 來實現所謂「記憶」的功能。能夠經過 React 組件的構造函數中設置 this.state;this.state 應該被視爲一個組件的私有屬性。
修改this.state值須要經過this.setState方法賦值,有些 props 值或 state 值多是異步更新的,使用對象賦值的方式更改 state 可能無效,可以使用回調傳參方式更新github

this.setState(  (state,props)=> ({count:state.count + props.count}) );

類組件

經過 class 語法來定義組件,必須包含render() 方法,而且繼承於 React.Component
類組件必須包含render(),而且return 只能返回一個父元素(相似vue中的template中必需要有一個父元素)。數組

class Square extends React.Component {
  /**
   * @name constructor
   * @param {*} props
   * 每次定義子類的構造函數時,都必須調用 super 方法。
   * 所以,在全部含有構造函數的React組件中,構造函數必須以super(props)開頭
   * state 保存着組件中的數據 相似 vue 中的 data 屬性
   */
  constructor(props) {
    super(props);
    this.state = {
      value:null
    };
  }
  render() {
    return (
      <button className="square" onClick = {()=>{this.setState({value:'X'})}
      }>
        {this.state.value}
      </button>
    );
  }
}

render方法中的onClick 事件監聽函數中調用this.setState方法,能夠設置this.state 中的屬性
推薦使用箭頭函數,避免this 形成困擾dom

簡單組件(函數組件)

簡單組件是一個函數,不須要使用class關鍵字,固然就沒有constructor和state異步

const Square =  (props) => {
 return  (<button className= "square"
   onClick= { props.onClick} >{ props.value  }
  </button>)
}

受控組件

input<select><textarea> 等表單的狀態發生改變,都得同時經過onChange事件改變組件中的state值,不然表單不會發生變化。經過這種方式控制取值的表單叫作受控組件

class Input extends Component {
  constructor(props){
    super(props)
    this.state = {
      value:'3s'
    }
  }
  render (){
    return <input type="text" value = {this.state.value} />   // 用戶在表單中輸入任何信息都是無效的
  }
}

// 使用事件來改變
 render (){
    return (
      <input
          type="text"
          value = {this.state.value}
          onChange = {({target}) =>{
            this.setState({
              value:target.value
            })
          }}
    />
    )
  }

React受控組件更新state的流程:

  • 經過在初始化state中設置表單默認值;
  • 每當表單值發生變化時,調用onChange事件
  • 事件經過合成的事件對象來改變狀態更新 state
  • setState觸發視圖渲染更新,完成表單組件值的更新

渲染多個組件

將組件變成數組集合放入花括號中便可渲染多個組件,一般使用數組的map()方法

const Lis = (props) => {
    const lis = props.list.map((val,key)=> <li key={key}>{key+1}</li>);
    return <ul>{lis}</ul>
}
const list = Array(7).fill(null);
ReactDOM.render(
  <Lis list = {list} />,
  document.getElementById('root')
);

狀態提高

當多個組件發生數據聯動時,建議將共享狀態提高到最近的共同父組件中去。

/*
 * @Author: Owen
 * @Date: 2019-07-23 23:55:17
 * @Last Modified by: Owen
 * @Last Modified time: 2019-07-29 16:06:22
 */


import React,{Component} from 'react';
import {render} from 'react-dom';

// 溫度轉化器
let toConvert = (temperature,callback) => {
  let num = parseInt(temperature);
  if(Number.isNaN(num)) return ''
   num = callback(num)
  return Math.round(num*1000)/1000;
}

const BoilingVerdict = (props) =>{
  let text = props.temperature > 100?'':' not';
  return (<p>
    The water would{text} boil.
  </p>)
}

// 公共input組件只接收行爲和狀態
const TemperatureInput = (props) =>{
  return (
    <input
    value ={props.temperature}
    onChange = {props.valueChange}
    />
  )
}
// 父組件設置行爲和狀態
class Calculator extends Component {
  constructor(props){
    super(props)
    this.state = {
      temperature:'',
      scale:'C'
    }
  }
  toFahrenheit({target}) {
    this.setState({
      temperature:target.value,
      scale:'F'
    })
  }
  toCelsius({target}) {
    this.setState({
      temperature:target.value,
      scale:'C'
    })
  }
  render() {
    let {temperature,scale} = this.state;
    let celsius = scale === 'F'?toConvert(temperature,(val)=>(val - 32)*5/9):temperature;
    let fahrenheit = scale === 'C'?toConvert(temperature,(val)=>val*9/5+32):temperature;

    return (
      <div>
        <div>
          Celsius:
          <TemperatureInput
          scale ='C'
          temperature = {celsius}
          valueChange = {this.toCelsius.bind(this)}
        />
        </div>

        <div>
          Fahrenheit:
          <TemperatureInput
          scale ='F'
          temperature = {fahrenheit}
          valueChange = {this.toFahrenheit.bind(this)}
        />
        </div>
        <div><BoilingVerdict temperature = {this.state.temperature} /></div>
      </div>

    );

  }
}
render(<Calculator />,document.querySelector('#root'))

在就React 應用中,任何科比數據應當只有一個相對應的數據源,一般,多個組件須要相同數據,能夠將數據提高到這些組件的共同父組件中。依靠自上而下的數據流,去控制組件,而不是嘗試在不一樣組件同步 state這樣會減小未來排查和隔離BUG所須要的工做量

組合(相似vue中的 slot)

有些組件沒法提早知曉它們子組件的具體內容,須要留坑,那麼也能夠經過 props來佔位。

默認坑位props.children

件起始標籤和結束標籤之間的內容都會被將{props.children}替換。

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}
function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

自定義坑位

由於 React元素 本質就是對象,因此能夠將它當中參數像其餘數據同樣傳遞。

function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

function App() {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      } />
  );
}

JSX

每一個 JSX 元素都是調用 React.createElement() 的語法糖。通常來講,若是你使用了 JSX,就再也不須要調用createElement()createFactory()方法。
React 提出的一種叫 JSX 的語法,這應該是最開始接觸 React 最不能接受的設定之一,由於前端被「表現和邏輯層分離」這種思想「洗腦」過久了。實際上組件的 HTML 是組成一個組件不可分割的一部分,可以將 HTML 封裝起來纔是組件的徹底體.

JSX是一個JavaScript語法擴展。它相似於模板語言,但它具備JavaScript 的所有能力。它最終會被編譯爲React.createElement()函數調用,返回稱爲 React元素的普通JavaScript`對象。

推薦使用箭頭函數,避免this 形成困擾

function Square(props) {
  return (
    < button className = "square"
             onClick = { props.onClick } >
      { props.value }
    </button>
  );
}

class Board extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      squares: Array(9).fill(null),
      xIsNext:true, // 先落子,並確認該哪位玩家落子
    }
  }
  /**
   * 只接受一個squares副本,而不直接修改自己數據
   * 1. 這樣能夠簡化複雜的功能,不可變性使得複雜的特性更容易實現。
   * 2. 能夠跟蹤數據的改變,若是直接修改源數據就很難跟蹤變化的數據。
   * 3. 能夠幫助咱們在 React 中建立 purecomponents。能夠輕鬆的肯定不可變數據是否發生了改變,
   *    從而肯定什麼時候對組件進行從新渲染。
   * @param {*} i
   * @memberof Board
   */
  handleClick(i) {
    const squares = this.state.squares.slice();
    squares[i] = this.state.xIsNext? "X":"O";
    this.setState({ squares,xIsNext:!this.state.xIsNext })
  }

  renderSquare(i) { // 返回一個 Square 組件
    return ( < Square
              value = { this.state.squares[i] }// 給子組件傳遞 value數據
              onClick = {()=> this.handleClick(i)} // 給子組件傳遞 onClick事件
        />);
  }


  render() {
    let {state} = this;
    const status = `Next player: ${state.xIsNext?'X':'O'}`;

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

在 JSX 中你能夠任意使用JavaScript表達式,只須要用一個大括號({})括起來;
事實上每一個 React 元素都是一個JavaScript 對象,能夠把它保存在變量中或者做爲參數傳遞。

爲避免遇到自動插入分號陷阱,最好將內容包裹在小括號中,若是隻有一行代碼則不須要括號

// 加括號
class App extends React.Component {
  render() {
    return (
      <div className="shopping-list">
        <h1>Shopping List for</h1>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

// 不用加括號
class App extends React.Component {
  render() {
    return  <li>Instagram</li>;
  }
}

JSX 語法更接近於 JavaScript,因此 ReactDom 使用cameCase(小駝峯命名)來定義屬性名稱,而且不要使用引號將大括號包裹,二者是不能並存的。對於字符串值使用引號,對於表達式使用大括號

React 中沒法經過 return false 的方式阻止默認行爲,必須使用e.preventDefault()阻止默認事件。可是不用擔憂event事件的兼容問題

JSX 自己就能防止XSS攻擊

// 原生DOM
<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

// JSX
class App extends React.Component {
  render() {
    return (
        <a href="#" onclick={(e)=>this.handleClick(e)}> //每次渲染時都會建立不一樣的回調函數。該回調函數做爲 prop 傳入子組件時,這些組件可能會進行額外的從新渲染。
            Click me
        </a>;
        <a href="#" onclick={(e)=>this.handleClick(id,e)}> // 向事件處理程序傳遞參數
            Click me
        </a>;
    )
  }
}

事件處理程序回調函數中的 this

  • JavaScript 中,class 的方法默認不會綁定 this。若是你忘記綁定 this.handleClick 並把它傳入了 onClick,當你調用這個函數的時候 this的值爲 undefined
class App extends React.Component {
    // 此語法確保 `handleClick` 內的 `this` 已被綁定。
    // 注意: 這是 **實驗性** 語法。 使用 Create React App 默認會啓用此語法
    handleClick = (e)=> {
        e.preventDefault();
        console.log(e)
    }
  render() {
    return (
        <a href="#" onclick={this.handleClick}>
            Click me
        </a>;
         <a href="#" onclick={this.handleClick。bind(this,id)}> // 向事件處理程序傳遞參數, 事件對象會被隱式傳遞
            Click me
        </a>;
    )
  }
}

Owen 的我的博客

參考資料
react 官網
React 入門教程

相關文章
相關標籤/搜索