Dvajs介紹

dva 是基於 redux 最佳實踐 實現的 framework,簡化使用 redux 和 redux-saga 時很多繁雜的操作
數據流向
數據的改變發生通常是通過用戶交互行爲或者瀏覽器行爲(如路由跳轉等)觸發的,當此類行爲會改變數據的時候可以通過 dispatch 發起一個 action,如果是同步行爲會直接通過 Reducers 改變 State ,如果是異步行爲(副作用)會先觸發 Effects 然後流向 Reducers 最終改變 State,所以在 dva 中,數據流向非常清晰簡明,並且思路基本跟開源社區保持一致

在這裏插入圖片描述

Modul
Subscription
Subscriptions 是一種從 源 獲取數據的方法,它來自於 elm。
Subscription 語義是訂閱,用於訂閱一個數據源,然後根據條件 dispatch 需要的 action。數據源可以是當前的時間、服務器的 websocket 連接、keyboard 輸入、geolocation 變化、history 路由變化等等。

subscriptions: {
  setup({ dispatch, history }) {
    history.listen(async ({ pathname }, action) => {
      const re =
        pathToRegexp('/group-member/list/:groupId').exec(pathname)
        ||
        pathToRegexp('/group-member/del/:groupId').exec(pathname)
      if (action !== 'POP' && re && re[1]) {
        const groupId = +re[1]
        dispatch({ type: 'initList' })
        dispatch({ type: 'fetchGroupMemberList', groupId })
      }
    })
  },
},

Effect
Effect 被稱爲副作用,在我們的應用中,最常見的就是異步操作。它來自於函數編程的概念,之所以叫副作用是因爲它使得我們的函數變得不純,同樣的輸入不一定獲得同樣的輸出。
dva 爲了控制副作用的操作,底層引入了redux-sagas做異步流程控制,由於採用了generator的相關概念,所以將異步轉成同步寫法,從而將effects轉爲純函數。

effects: {
  * fetchGroupMemberList({ groupId }, { call, put }) {
    try {
      const { succeed, data: { role, member: { list: briefs } } } =
        yield call(fetch.get, `${GROUP_MEMBER_URL}/${groupId}/1/${GROUP_MEMBER_PAGE_SIZE}`)
      if (succeed) {
        yield put({ type: 'nextList', briefs, page: 1 })
        yield put({ type: 'setIdAndRole', role })
      }
    } catch (err) {
      console.log('Error when fetch group member list', err.stack)
      yield put({ type: 'app/showToast', title: '獲取羣組成員列表錯誤' })
    }
  },
  ...
},

Reducer
在 dva 中,reducers 聚合積累的結果是當前 model 的 state 對象。通過 actions 中傳入的值,與當前 reducers 中的值進行運算獲得新的值(也就是新的 state)。需要注意的是 Reducer 必須是純函數,所以同樣的輸入必然得到同樣的輸出,它們不應該產生任何副作用。並且,每一次的計算都應該使用immutable data,這種特性簡單理解就是每次操作都是返回一個全新的數據(獨立,純淨),所以熱重載和時間旅行這些功能才能夠使用。

reducers: {
  initList(state) {
    console.log('initLists')
    return {
      ...state,
      list: [],
    }
  },
  ...
},

State
State 表示 Model 的狀態數據,通常表現爲一個 javascript 對象(當然它可以是任何值);操作的時候每次都要當作不可變數據(immutable data)來對待,保證每次都是全新對象,沒有引用關係,這樣才能保證 State 的獨立性,便於測試和追蹤變化

state: {
  id: 0,
  title: '全部成員',
  list: [],
  briefs: {},
  itemCount: 1,
  isManager: false,
},

Action
Action 是一個普通 javascript 對象,它是改變 State 的唯一途徑。無論是從 UI 事件、網絡回調,還是 WebSocket 等數據源所獲得的數據,最終都會通過 dispatch 函數調用一個 action,從而改變對應的數據。action 必須帶有 type 屬性指明具體的行爲,其它字段可以自定義,如果要發起一個 action 需要使用 dispatch 函數;需要注意的是 dispatch 是在組件 connect Models以後,通過 props 傳入的
dispatch({ type: ‘initList’ })

dispatching function 是一個用於觸發 action 的函數,action 是改變 State 的唯一途徑,但是它只描述了一個行爲,而 dipatch 可以看作是觸發這個行爲的方式,而 Reducer 則是描述如何改變數據的。
在 dva 中,connect Model 的組件通過 props 可以訪問到 dispatch,可以調用 Model 中的 Reducer 或者 Effects
Route Components
在 dva 中我們通常將其約束爲 Route Components,因爲在 dva 中我們通常以頁面維度來設計 Container Components。
所以在 dva 中,通常需要 connect Model的組件都是 Route Components,組織在/routes/目錄下,而/components/目錄下則是純組件(Presentational Components)

class GroupMember extends PureComponent {
  static propTypes = {
    groupId: PropTypes.number.isRequired,
    title: PropTypes.string,
    list: PropTypes.arrayOf(PropTypes.number),
    fetchNext: PropTypes.func,
    briefs: PropTypes.instanceOf(Object),
    removeMember: PropTypes.func,
    showConfirm: PropTypes.func,
    url: PropTypes.string.isRequired,
  }

  static defaultProps = {
    title: '',
    list: [],
    fetchNext: () => {},
    briefs: {},
    isManager: false,
    removeMember: () => {},
    showConfirm: () => {},
  }

  submitRemoveMember = id => () => {
    const { removeMember, groupId } = this.props
    removeMember(groupId, id)
  }

    ...

  render() {
    const { title, list, fetchNext, groupId } = this.props
    return (
      <Container>
        <Navigator
          title={title}
        />
        <MemberListView
          ref={(listView) => { this.listView = listView }}
          dataSource={ds.cloneWithRows(list)}
          renderRow={this.renderMember}
          enableEmptySections
          onEndReached={() => fetchNext(groupId)}
          onEndReachedThreshold={100}
        />
      </Container>
    )
  }
}

connect
通過connect將modul中的元素作爲props的方式傳遞給component
export default connect(({ groupMember, app }, { location }) => {
  const url = location.pathname
  const groupId = +location.params.groupId
  const userId = app.userInfo.userId
  let list = [...groupMember.list]
  let briefs = { ...groupMember.briefs }
  if (url.indexOf('del') !== -1) {
    list = list.filter(each => (each !== userId))
    briefs = R.dissoc(userId, briefs)
  }
  return { ...groupMember, groupId, url, list, briefs }
}, { ...actions, ...appActions })(GroupMember)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章