react 全家桶 實現 淘寶返利提現小項目

第一次真正使用 react 去作一個小項目,可能有使用的不合理的地方,能夠在 issue 中提意見,如下記錄了項目實現過程當中遇到的問題。

項目介紹

模仿如今比較火的,淘寶返利公衆號的功能,包含訂單頁面,已付、待反利、已反、已失效,提現頁面,提現記錄以及登陸頁。其中部分頁面&使用方式參照react-pxq項目,star 數量 6359 頗有參考意義html

1.使用 react 建立頁面 √前端

2.使用 react-router-dom 實現路由 √node

3.使用 props-type 作屬性檢查 √react

4.搭建 mock server 模擬數據請求 √webpack

5.使用 redux 實現狀態管理 √ios

6.使用 Immutablegit

7.項目中添加異步請求 √github

8.redux-thunk 中間件使用 √web

111

111


項目使用

npm start

在開發環境運行chrome

瀏覽器打開http://localhost:3000可訪問

npm run build

將工程打包到 build 目錄下

npm run eject

create-react-app 自己的 webpack 配置文件存在於 node_modules/react-scripts/目錄下面,可是這個目錄是 node_modules/,裏面的源碼都是不建議修改的。create-react-app 提供了 eject 命令,用於釋放這些配置。

並且 create-react-app 並不推薦你們這麼作,由於這個步驟沒法逆轉!

npm run eject 以後,react-scripts 命令就失效了哦。由於在 node_modules/下面,都沒有 react-scripts/的目錄了,它以另外的形式存在於這個項目裏面了。


項目 UI 組件

使用 Ant Design ,相比原生 Html 能快速搭建頁面,更專一於 react 相關技術的學習,其實寫的是 app 端的淘寶返利一個小項目,後來發現應該使用 Ant Design Mobile 來作移動端的,不過沒關係,咱們的注意力不在頁面適配上。


React 開發者工具

下載 chrome 插件,方便 react 項目代碼調試


React 類型檢查

react 的類型檢查 PropTypes 自 React v15.5 起已棄用,請使用prop-types

JavaScript 是一門弱類型的語言,容許變量類型作隱式轉換。也正是由於這個特性,JavaScript 中有不少錯誤都是類型錯誤致使的。爲了減小這種錯誤,咱們能夠在 React 中引入類型檢查模塊。

經常使用的檢查類型有一下幾種:

// 屬性能夠聲明爲 JS 原生類型
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol

react-redux 狀態管理

React-Redux 將全部組件分紅兩大類:UI 組件(presentational component)和容器組件(container component)。

UI 組件負責 UI 的呈現,容器組件負責管理數據和邏輯

UI 組件

  • 只負責 UI 的呈現,不帶有任何業務邏輯
  • 沒有狀態(即不使用 this.state 這個變量)
  • 全部數據都由參數(this.props)提供
  • 不使用任何 Redux 的 API

容器組件

  • 負責管理數據和業務邏輯,不負責 UI 的呈現
  • 帶有內部狀態
  • 使用 Redux 的 API

connect

React-Redux 提供 connect 方法,用於從 UI 組件生成容器組件。connect 的意思,就是將這兩種組件連起來。

// CashOut.jsx
import { connect } from 'react-redux'
class CashOut extends Component {}
export default connect(
  state => ({
    cashInfo: state.cashInfo
  }),
  { addToCashList, resetUseMoney }
)(CashOut)

爲了定義業務邏輯,須要給出下面兩方面的信息。

  • 輸入邏輯:外部的數據(即 state 對象)如何轉換爲 UI 組件的參數
    eg:cashInfo
  • 輸出邏輯:用戶發出的動做如何變爲 Action 對象,從 UI 組件傳出去。
    eg: addToCashList resetUseMoney

Provider 組件

connect 方法生成容器組件之後,須要讓容器組件拿到 state 對象,才能生成 UI 組件的參數。
React-Redux 提供 Provider 組件,可讓容器組件拿到 state。

import { Provider } from 'react-redux'
import store from '@/store/store'

const render = Component => {
  ReactDOM.render(
    //綁定redux、熱加載
    <Provider store={store}>
      <Component />
    </Provider>,
    document.getElementById('root')
  )
}

render(Route)

mock server 搭建

使用 mocker-api coss-env

在 webpack-dev-server 的 before 鉤子函數中搭建服務器

運行:npm run start-mock

訪問:localhost:3000/api/getExtractList 查看數據


axios 異步請求

src 下新建 api 文件

|-src
|-|-api
|-|-|-api.js // 用來寫接口
|-|-|-server.js // 是對axios的封裝

server.js

axios 中文文檔 查看 axios 配置項

import axios from 'axios'

const TIMEOUT = 30000 // 設置超時時間

export default class Server {
  axios(method, url, params) {
    return new Promise((resolve, reject) => {
      if (typeof params !== 'object') params = {}
      const _option = {
        method,
        url,
        baseURL: 'http://localhost:3002',
        timeout: TIMEOUT,
        params: null,
        data: null,
        headers: null,
        withCredentials: true, //是否攜帶cookies發起請求
        validateStatus: status => {
          return status >= 200 && status < 300
        },
        ...params
      }
      axios.request(_option).then(
        res => {
          resolve(
            typeof res.data === 'object' ? res.data : JSON.parse(res.data)
          )
        },
        error => {
          if (error.response) {
            reject(error.response.data)
          } else {
            reject(error)
          }
        }
      )
    })
  }
}

api.js

import Server from './server'

class API extends Server {
  async getExtractList(data = {}) {
    try {
      let result = await this.axios('post', '/api/getExtractList', data)
      return result
    } catch (err) {
      throw err
    }
  }
}

export default new API()

redux-thunk

一個關鍵問題沒有解決:異步操做怎麼辦?Action 發出之後,Reducer 當即算出 State,這叫作同步;Action 發出之後,過一段時間再執行 Reducer,這就是異步。

怎麼才能 Reducer 在異步操做結束後自動執行呢?這就要用到新的工具:中間件(middleware)。

Redux 的核心概念其實很簡單:將須要修改的 state 都存入到 store 裏,發起一個 action 用來描述發生了什麼,用 reducers 描述 action 如何改變 state tree 。建立 store 的時候須要傳入 reducer,真正能改變 store 中數據的是 store.dispatch API。

import { createStore, combineReducers, applyMiddleware } from 'redux'
import * as cashout from './cashout/reducer'
import thunk from 'redux-thunk'

let store = createStore(
  combineReducers({ ...cashout }),
  applyMiddleware(thunk) // 使用中間件
)

export default store

store/action.js

// 獲取提現記錄列表,保存至redux
export const getCashList = () => {
  return async dispatch => {
    try {
      const value = await API.getCashList()
      // 經過dispatch來更新store
      dispatch({
        type: GETCASHLIST,
        value,
        initLoading: false
      })
    } catch (err) {
      console.error(err)
    }
  }
}

問題記錄

1.函數傳參數保留 event

<p className="my-drawer" onClick={this.goTo.bind(this,'/home')} >首頁</p>
goTo (path,e){
    this.setState({
      visible: false,
    });
    this.props.history.push(path)
  };

2.子組件修改父組件中 state 方法

爲了實現需求網上搜到一種,就用在項目中。看到 React 官方文檔上提供的【狀態提高】溫度的例子,也是經過父組件提供函數,以 pros 形式傳遞給子組件,子組件調用 props 來修改父組件的 state。

父組件代碼以下:

class CashOut extends Component {
  // 在父組件中定義能夠改變state值得函數
  transferVisible(visible) {
    this.setState({
      visible
    })
  }
  // 把transferVisible函數做爲屬性傳遞給Dialog子組件上 如 transferVisible
  render() {
    return (
      <div>
        <Dialog
          title="提現成功"
          des="您以提現成功,可到提現記錄中查看"
          visible={this.state.visible}
          transferVisible={visible => this.transferVisible(visible)}
        ></Dialog>
      </div>
    )
  }
}
export default CashOut

子組件代碼以下:

class Dialog extends React.Component {
  // 點擊確認和取消時 調用props修改父組件state值
  handleCancel = () => {
    this.props.transferVisible(false)
  }
  render() {
    return (
      <div>
        <Modal
          title={this.props.title}
          visible={this.props.visible}
          okText="確認"
          cancelText="取消"
          onOk={this.handleCancel}
          onCancel={this.handleCancel}
        >
          <p>{this.props.des}</p>
        </Modal>
      </div>
    )
  }
}
export default Dialog

3.跨域請求預檢

在個人訂單中,已付、待反 4 個 Tab 頁面請求都會發送兩次

(1)Request Method: OPTIONS
(2)Request Method: POST

跨域分爲:

1.簡單跨域
2.複雜跨域:複雜跨域會進行預檢

複雜跨域在發送真正的請求前, 會先發送一個方法爲 OPTIONS 的預請求(preflight request), 用於試探服務端是否能接受真正的請求,若是 options 得到的迴應是拒絕性質的,好比 404403500 等 http 狀態,就會中止 post、put 等請求的發出。

有三種方式會致使這種現象:

  • 請求方法不是 GET/HEAD/POST
  • POST 請求的 Content-Type 並不是 application/x-www-form-urlencoded, multipart/form-data, 或 text/plain
  • 請求設置了自定義的 header 字段

個人項目中出現這種狀況是由於使用 POST 請求,Content-Type 設置了 application/json

解決方法:

(1)POST 請求,Content-Type 設置 application/x-www-form-urlencoded
(2)使用 qs 對 object 進行轉換


參考

  1. create-react-app 工程,如何經過 eject 釋放配置文件?
  2. React-Redux 的用法
  3. Redux 官方文檔
  4. react-redux 工做原理
  5. Immutable 介紹學習
  6. Redux 中文文檔
  7. 前端技術總結 Redux 部分
  8. 初學者的 React 全家桶完整實例
  9. 關於 React 全家桶的介紹
  10. React 學習系列——prop 和 state
  11. react-router 之嵌套路由
  12. create-react-app 入門教程
  13. react 中使用 prop-types 檢測 props 數據類型
  14. react 中文文檔
  15. react-pxq
  16. react + redux 完整的項目,同時寫一下我的感悟
  17. 使用 immutable 優化 React
  18. Redux 中間件之 redux-thunk 使用詳解
  19. Redux 入門教程(二):中間件與異步操做
相關文章
相關標籤/搜索