React學習筆記

1、React初探

es6寫法 code
import React from 'react';
  import ReactDOM from 'react-dom';
  import PropTypes from 'prop-types';
  
  class App extends React.Component { 
      state = {
          title: '環球大前端'
      }
      render() {
          const { title } = this.state;
          const { name } = this.props
          return (
            <div>
                <h2>{title}</h2>
                <p> Hello {name}! </p>
            </div>
          )
      }
  }
  App.propTypes = {
      name: PropTypes.string
  }
  App.defaultProps = {
      name: '帥氣小夥子'
  }
  ReactDOM.render(<App name="24小清新" />, document.getElementById('app'));
es5寫法(遺憾的是如今最新版本的react,已經再也不能使用createClass去建立react組件了,因爲react將createClass剝離出去,減小react的體積,咱們可使用create-react-class 模塊來建立 code
var App = React.createClass({
    getDefaultProps: function() {
      return {
        name: '帥氣小夥子'  
      }  
    },
    getInitialState: function() {
        return {
            title: '環球大前端'
        }
    },
    render: function() {
      return (
        <div>
            <h2>{this.state.title}</h2>
            <p> Hello {this.props.name}! </p>
        </div>
      )
    }
})
React.render(<App name="24小清新" />, document.getElementById('app'));

核心思想:封裝組件,各個組件維護本身的狀態(state, prop)和UI,當狀態變動,自動從新渲染組件,數據流向是單向的。javascript

須要明白的幾個基礎概念:html

一、什麼是JSX?前端

二、如何修改組件state,從而修改組件UI?java

三、事件處理react

對於上述那些既不是字符串也不是 HTML的的標籤語法,被稱爲JSX,是一種 JavaScript 的語法擴展,用來描述用戶界面。es6

經常使用的是在JSX中使用表達式,例如 2 + 2, user.firstName, 以及 formatName(user),條件判斷(三目運算符、&&), 數組Map函數遍歷獲取React元素 都是可使用的。如:ajax

formatName(user) {
        return `${user.firstName}-${user.name}`;
    }
    const user = {
        firstName: 'wu',
        name: 'shaobin'
    }
    const show = true; //我能夠是this.state屬性哦!!!
    const arr = ['xiaobin', 'kaizi', 'liujun'];
    const element = (
      <div>
        <h1>Hello, {formatName(user)}!</h1>
        <h1>Hello, {user.name}!</h1>
        <h1>Hello, { 1 + 1 }!</h1>
        <h1>Hello, { show ? 'I am show' : null }</h1>
        <h1>Hello, { arr.length > 0 && <span>數組長度大於0</span> }</h1>
        {
            arr.map((item, index) => {
                return <span key={item}>item</span>    
            })
        }
        //記住數組Map函數遍歷獲取React元素的時候,必需要記得必須➕keys屬性
        // 爲啥呀?
        //Keys能夠在DOM中的某些元素被增長或刪除的時候幫助React識別哪些元素髮生了變化。所以你應當給數組中的每個元素賦予一個肯定的標識。沒有惟一值的時候可使用index,可是官方不建議,會致使渲染變慢。
      </div>
    );
    ReactDOM.render(
      element,
      document.getElementById('root')
    );

切記每一個組件的render函數返回的JSX結構都須要根元素去包裹着,固然也有例外,如React.Fragmentredux

對於JSX,react最終經babel的轉換會調用React.createElement相應api轉換成react能識別的對象,如上述例子轉換後獲得:api

React.createElement(
    'div',
    null,
    React.createElement(
        'h2', //能夠是一個html標籤名稱字符串,也能夠是也能夠是一個 React component 類型
        null,
        title
    ),
    React.createElement(
        'p',
        null,
        ' Hello ',
        name,
        '! '
    )
);

babel查看es6->es5的結果數組

既然咱們能夠爲組件初始化狀態,也必需要可以去改變它,以達到改變視圖。
固然this.state.xxx = xxx不會觸發渲染組件的動做,而是使用this.setState({ xxx: xxx })方法來修改狀態,同時多個setState() 調用合併成一個調用能提升性能。

對於事件處理,須要注意的一點就是this的綁定,其餘跟普通Dom綁定監聽事件同樣,this的綁定有如下幾種方式:

  1. 在構造函數中使用bind綁定this
  2. 在調用的時候使用bind綁定this
  3. 在調用的時候使用箭頭函數綁定this
  4. 使用屬性初始化器語法綁定this

setState與事件處理的例子

同時須要注意的是,React合成事件(onClick={})和原生事件(document.addEventListener)的阻止冒泡存在差別,須要明白的是,全部合成事件都是綁定在document上的(代理),因此執行合成事件中的event.stopPropagation(),實際原生事件仍是會冒泡到document上,同時須要注意的是,setState 只在合成事件和生命週期函數中是 "異步" 的,在原生事件和 setTimeout 中都是同步的。
合成事件與原生事件列子
參考文章:
從 Dropdown 的 React 實現中學習到的
React合成事件和DOM原生事件混用須知

2、React進階

一、有哪些生命週期,生命週期的執行順序?

二、Ref的引用

三、高階組件的使用

生命週期圖示(React16):

Mounting/掛載

constructor()    //React組件的構造函數,用於super(props),初始化state,bind綁定事件等

static getDerivedStateFromProps()

UNSAFE_componentWillMount()    // 組件掛載前(組件渲染到頁面前)

render()    // 渲染函數,不作實際的渲染動做,它只是返回一個JSX描述的結構,生成虛擬Dom樹,執行patch,最終由React來操做渲染過程

componentDidMount()  //組件掛載後(組件渲染到頁面上),能夠在這個鉤子添加異步請求以及定時器,或是socket鏈接

Updating/更新

UNSAFE_componentWillReceiveProps() // 組件接收到屬性時觸發

static getDerivedStateFromProps()

shouldComponentUpdate(prevProps, prevState)  // 當組件接收到新屬性,或者組件的狀態發生改變時觸發。組件首次渲染時並不會觸發,此鉤子可作性能優化

UNSAFE_componentWillUpdate()  //組件即將被更新時觸發

render() // 渲染函數,不作實際的渲染動做,它只是返回一個JSX描述的結構,生成虛擬新Dom樹,與舊樹進行diff, 執行patch

getSnapshotBeforeUpdate()

componentDidUpdate(prevProps, prevState, snapshot) // 組件被更新完成後觸發,生命週期中因爲state的變化觸發請求,在componentDidUpdate中進行

Unmounting/卸載

componentWillUnmount()  // 卸載組件,註銷監聽事件或是定時器,socket關閉

詳細看生命週期例子

補充Tip

getSnapshotBeforeUpdate()static getDerivedStateFromProps()兩個新增生命週期鉤子是被用來代替 UNSAFE_componentWillMount() ,UNSAFE_componentWillUpdate(), UNSAFE_componentWillReceiveProps()三個生命週期的,可是這個三個生命週期還是可使用。爲何勒?React爲了1.7版本實現Async Rendering。

Refs 提供了一種方式,用於訪問在 render 方法中建立的 DOM 節點或 React 元素,官方建議少用。獲取Ref有三種場景:


獲取Ref的經常使用方式(經過this.myRef.current來獲取Dom節點或實例):

class MyComponent extends React.Component {
   constructor(props) {
     super(props);
     this.myRef = React.createRef(); // 調用React.createRef API
   }
   render() {
     return <input ref={this.myRef} />;
   }
}
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <input ref={(ref) => { this.myRef = ref; }} />;
  }
}

Ref獲取Dom元素例子, Ref獲取React元素(子組件實例)例子

補充Tip: 

對於第三種狀況,獲取子組件的Dom節點,官方有提供Forwarding Refs(轉發Ref)的方法,來獲取子組件的Dom節點的Ref,此方法返回的是一個React元素,對應方法爲
React.forwardRef((props, ref) => { ... })

Ref獲取子組件Dom元素或是React元素的ref例子

高階組件(HOC)是react中對組件邏輯進行重用的高級技術。但高階組件自己並非React API。它只是一種模式,其實就是一個函數,且該函數接受一個組件做爲參數,並返回一個新的組件。高階組件在React第三方庫中很常見,好比Redux的connect方法和react-router的withRouter()方法。

注意事項:

一、不要再render函數中使用高階組件,否則會致使每次從新渲染,都會從新建立高階組件實例,銷燬掉舊的高階組件,致使全部狀態和子組件都被卸載。

二、必須將靜態方法作拷貝,當使用高階組件包裝組件,原始組件被容器組件包裹,獲得新組件會丟失原始組件的全部靜態方法,假如原始組件有靜態方法,可使用hoist-non-react-statics進行靜態方法拷貝。例子

三、Refs屬性不能傳遞,高階組件能夠傳遞全部的props屬性給包裹的組件,可是不能傳遞refs引用,可是有時候咱們確實須要把ref的引用傳給包裹組件,能夠傳一個非ref命名的props屬性給到高階組件上,由高階組件綁定到包裹組件的ref上,也可使用轉發Ref。例子

3、React第三方庫

一、Reduxreact-redux

Redux主要分紅三部分,分別爲Store,Action,Reducer,下面是對三部分的通俗的講解:

Store:Redux應用只有一個單一的Store,就是單一數據源,將整個應用共享的狀態state儲存在一棵對象樹上面,注意的是,對象樹上面的state是隻讀的,只能經過純函數來執行修改,建立store,是經過Redux的 createStore(reducer)方法來建立的,store裏面會有getState()、dispatch()、subscribe(listener)的方法。

Action:一個普通的Javascript對象,描述了應用state發生了什麼變化,經過dispatch方法來通知store調用reducer方法。

Reducer:描述應用如何更新state,自己是一個函數,接受Action參數,返回新的state。

不結合react-redux的Redux使用例子

須要明白的一點Redux跟React一點關係都沒有,可是React搭配Redux來實現狀態管理時最好的實現方案。那麼如何搭配呢?原本咱們能夠subscribe(listener)在react的組件註冊redux的監聽器,可是這種方式繁瑣,並且會致使屢次渲染。因此搭配着react-redux來使用。基本使用以下:

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import App from './components/App'

const todoApp = (state = {}, action) {
   if (actions.type === 'SHOW') {
       return Object.assign({}, state, {
        show: action.show
       });
   }
   return state;
}

let store = createStore(todoApp)
render(
  // 使用指定的 React Redux 組件 <Provider> 來讓全部容器組件均可以訪問 store,而沒必要顯示地傳遞它。只須要在渲染根組件時使用便可。
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)
import React from 'react'
import { connect } from 'react-redux';
import Child from './components/Child'
class App extends Component {
    render() {
        const { show } = this.props;
        return (
            <div>
                <Child show={show }/>
            </div>
        );
    }
}
const stateToProps = (state) => ({
    show: state.show
});

export default connect(stateToProps)(App);

結合react-redux的例子

react-redux內容實現原理,使用的Context API,簡單來講,父組件聲明須要跨層級組件傳遞的屬性(childContextType)以及監聽屬性變化的getChildContext()函數,子組件聲明能夠接收上層組件傳遞的屬性(contextType)。

若是存在多個reducer的話,可使用redux中的combineReducers進行合併,代碼以下:

import { createStore, combineReducers } from 'redux';
const todoApp = combineReducers({
  reducer1,
  reducer2
})
const store = createStore(todoApp);

代碼等價於:

import { createStore} from 'redux';
const todoApp = (state = {}, action) => {
  return {
    reducer1: state.reducer1,
    reducer2: state.reducer2
  }
}
const store = createStore(todoApp);

combineReducers最終只是生成一個函數,這個函數來調用你的一系列 reducer,每一個 reducer 根據它們的 key 來篩選出 state 中的一部分數據並處理,而後這個生成的函數再將全部 reducer 的結果合併成一個大的對象。詳細邏輯能夠看redux源碼

二、react-router

對於路由規則,咱們在項目裏面搭配的是react-router v4這個庫來完成的,因爲咱們這以前也沒接觸過react-router,因此版本v3與v4之間模式和策略的差別不一樣也沒有帶來思惟模式轉換的困難,下面先帖碼簡單看看v3與v4版本之間的差別性(摘自掘金):

import { Router, Route, IndexRoute } from 'react-router'
const PrimaryLayout = props => (
    <div className="primary-layout">
        <header>
          Our React Router 3 App
        </header>
        <main>
          {props.children}
        </main>
  </div>
)
const HomePage =() => <div>Home Page</div>
const UsersPage = () => <div>Users Page</div>
const App = () => (
  <Router history={browserHistory}>
    <Route path="/" component={PrimaryLayout}>
      <IndexRoute component={HomePage} />
      <Route path="/users" component={UsersPage} />
    </Route>
  </Router>
)
render(<App />, document.getElementById('root'))
import { BrowserRouter, Route } from 'react-router-dom'

const PrimaryLayout = () => (
    <div className="primary-layout">
    <header>
      Our React Router 4 App
    </header>
    <main>
      <Route path="/" exact component={HomePage} />
      <Route path="/users" component={UsersPage} />
    </main>
  </div>
)

const HomePage =() => 
<div>Home Page</div>

const UsersPage = () => 
<div>Users Page</div>


const App = () => (
  <BrowserRouter>
    <PrimaryLayout />
  </BrowserRouter>
)

render(<App />, document.getElementById('root'))

差別性:

一、v3是集中性路由,全部路由都是集中在一個地方, 而v4則相反。

二、v3佈局和頁面嵌套是經過 組件的嵌套而來的,而v4不會互相嵌套

三、v3佈局和頁面組件是徹底純粹的,它們是路由的一部分,而v4路由規則位於佈局和 UI 自己之間

四、使用v4須要在咱們的組件根部用BrowserRouter組件(用於瀏覽器)去包裝,實現原理與react-redux的Provider組件同樣(Context API),以便組件能夠去拿到路由信息。

這裏主要介紹包容性路由、排他性路由、嵌套路由,以及withRouter的一些基本用法。

相關文章
相關標籤/搜索