React項目實戰:react-redux-router基本原理

React相關

React 是一個採用聲明式,高效並且靈活的用來構建用戶界面的框架。javascript

JSX

本質上來說,JSX 只是爲React.createElement(component, props, ...children)方法提供的語法糖。好比下面的代碼:前端

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

編譯爲:java

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

React.createElement()這個方法首先會進行一些避免bug的檢查,以後會返回一個相似下面例子的對象:react

const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world'
  }
};

這樣的對象被稱爲React元素,它表明全部你在屏幕上看到的東西。
咱們用 React 開發應用時通常只會定義一個根節點。要將 React 元素渲染到根DOM節點中,咱們經過把它們都傳遞給ReactDOM.render()的方法來將其渲染到頁面上:git

ReactDOM.render(
  element,
  document.getElementById('root')
);

每當 React 元素髮生變化時,ReactDOM首先會比較元素內容前後的不一樣,而後操做瀏覽器DOM更新改變了的部分。github

組件 & Props

當 React 遇到的元素是用戶自定義的組件,它會將 JSX 屬性做爲單個對象傳遞給該組件,這個對象稱之爲props。不管是使用函數或是類來聲明一個組件,它決不能修改它本身的 props 。
例如,這段代碼會在頁面上渲染出Hello,Sara:redux

//使用 ES6 class 來定義一個組件,組件名稱必須以大寫字母開頭。
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

咱們來回顧一下在這個例子中發生了什麼:瀏覽器

  1. 咱們對<Welcome name="Sara" />元素調用了ReactDOM.render()方法。
  2. React 將{name: 'Sara'}做爲props傳入並調用 Welcome 組件。
  3. Welcome 組件將<h1>Hello, Sara</h1>元素做爲結果返回。
  4. ReactDOM 將DOM更新爲<h1>Hello, Sara</h1>

State & 生命週期

組件的經過props獲取屬性,且其不能修改;當咱們須要修改當前組件的狀態時,要用到state來設置局部狀態,須要經過this.setState()來更新組件局部狀態:服務器

class Toggle extends React.Component {
  constructor(props) {
    super(props);    //初始化this,並賦值this.props
    this.state = {isToggleOn: true};    //初始化this.state
    this.handleClick = this.handleClick.bind(this);    //爲this.handleClick綁定this對象
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));    //用this.setState()更新this.state
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

每個組件都有幾個你能夠重寫以讓代碼在處理環節的特定時期運行的「生命週期方法」。方法中帶有前綴will的在特定環節以前被調用,而帶有前綴did的方法則會在特定環節以後被調用。網絡

  • 裝配:這些方法會在組件實例被建立和插入DOM中時被調用:

    - constructor(`props`)
    - componentWillMount()
    - render()
    - componentDidMount()
  • 更新:屬性或狀態的改變會觸發一次更新。當一個組件在被重渲時,這些方法將會被調用:

    - componentWillReceiveProps(`nextProps`)
    - shouldComponentUpdate(`nextProps`, `nextState`)
    - componentWillUpdate(`nextProps`, `nextState`)
    - render()
    - componentDidUpdate(`prevProps`, `prevState`)
  • 卸載:當一個組件被從DOM中移除時,該方法被調用:

    - componentWillUnmount()

當項目視圖交互複雜且頻繁的時候,依舊採用 state 進行狀態更改會顯得異常繁瑣和不可預測。
這時咱們就須要藉助 Redux 框架,將狀態數據所有轉交給 Redux 處理,React 專注負責視圖顯示,這樣會讓項目邏輯變得簡單而清晰。

Redux相關

三大原則:

  • 整個應用的 state 被儲存在一棵 object tree 中,而且這個 object tree 只存在於惟一一個store中。
  • 唯一改變 state 的方法就是觸發action,action 是一個用於描述事件的普通對象。
  • 爲了描述 action 如何改變 state tree ,你須要編寫reducers

Action

Action 是把數據從項目傳到 store 的有效載荷。它是 store 數據的惟一來源。一般你會經過store.dispatch()將 action 傳到 store。

Action 本質上是 JavaScript 普通對象,添加新 todo 任務的 action 是這樣的:

{
  type: 'ADD_TODO',
  text: 'Build my first Redux app'
}

Action 建立函數就是生成 action 的方法。在 Redux 中的 action 建立函數只是簡單的返回一個 action:

function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text: text
  }
}

這樣作將使 action 建立函數更容易被移植和測試。只需把 action 建立函數的結果傳給 dispatch() 方法便可發起一次 dispatch 過程。

dispatch(addTodo(text));

//或者建立一個 被綁定的 action 建立函數 來自動 dispatch:
const boundAddTodo = (text) => dispatch(addTodo(text));
boundAddTodo(text);

store 裏能直接經過 store.dispatch() 調用 dispatch() 方法,可是多數狀況下你會使用 react-redux 提供的connect()幫助器來調用。

Reducer

Action 只是描述了有事情發生了這一事實,而reducer要作的事情正是指明應用如何更新 state 。reducer 就是一個純函數,接收舊的 state 和 action,返回新的 state。

(previousState, action) => newState

保持 reducer 純淨很是重要。永遠不要在 reducer 裏作這些操做:

  • 修改傳入參數;
  • 執行有反作用的操做,如 API 請求和路由跳轉;
  • 調用非純函數,如 Date.now() 或 Math.random()。

咱們將以指定 state 的初始狀態做爲開始。Redux 首次執行時,state 爲 undefined,此時咱們可藉機設置並返回應用的初始 state:

const initialState = {};    //初始化state

function todoApp(state = initialState, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return Object.assign({}, state, {
        text: action.text
      })
    default:
      return state    //在 default 狀況下返回舊的 state
  }
}

每一個 reducer 只負責管理全局 state 中它負責的一部分。每一個 reducer 的 state 參數都不一樣,分別對應它管理的那部分 state 數據。

combineReducers()所作的只是生成一個函數,這個函數來調用你的一系列 reducer,每一個 reducer 根據它們的 key 來篩選出 state 中的一部分數據並處理,而後這個生成的函數再將全部 reducer 的結果合併成一個大的對象。

import { combineReducers } from 'redux';

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp;

注意上面的寫法和下面徹底等價:

export default function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}

combineReducers 接收一個對象,能夠把全部頂級的 reducer 放到一個獨立的文件中,經過 export 暴露出每一個 reducer 函數,而後使用 import * as reducers 獲得一個以它們名字做爲 key 的 object:

import { combineReducers } from 'redux'
import * as reducers from './reducers'

const todoApp = combineReducers(reducers)

Store

action 描述發生了什麼,reducers 根據 action 更新 state,Store就是把它們聯繫到一塊兒的對象。Store 有如下職責:

  • 維持應用的 state;
  • 提供getState()方法獲取 state;
  • 提供dispatch(action)方法更新state;
  • 經過subscribe(listener)註冊監聽器;
  • 經過subscribe(listener)返回的函數註銷監聽器。

咱們使用 combineReducers() 將多個 reducer 合併成爲一個。如今咱們將其導入,並傳遞 createStore()

import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

createStore() 的第二個參數是可選的, 用於設置 state 初始狀態。這對開發同構應用時很是有用,服務器端 redux 應用的 state 結構能夠與客戶端保持一致, 那麼客戶端能夠將從網絡接收到的服務端 state 直接用於本地數據初始化。

let store = createStore(todoApp, window.STATE_FROM_SERVER);

數據流

Redux 應用中數據的生命週期遵循下面 4 個步驟:

  1. 調用 store.dispatch(action)。
  2. Redux store 調用傳入的 reducer 函數。
  3. 根 reducer 應該把多個子 reducer 輸出合併成一個單一的 state 樹。
  4. Redux store 保存了根 reducer 返回的完整 state 樹。

Router相關

直接使用整合後的react-router-redux,後面抽時間再詳細講一下,具體使用的話模仿官方案例吧,官方文檔

容器組件 和 展現組件

Redux 的 React 綁定庫包含了 容器組件和展現組件相分離 的開發思想。

明智的作法是隻在最頂層組件(如路由操做)裏使用 Redux。其他內部組件僅僅是展現性的,全部數據都經過 props 傳入。

組件分離

系列目錄

  1. 前端大統一時代即未來臨?
  2. React項目實戰:環境搭建
  3. React項目實戰:react-redux-router基本原理
  4. React項目實戰:登陸頁面(編輯中)
相關文章
相關標籤/搜索