normalizr 学习笔记 --- node.js开发

5 篇文章 0 订阅
2 篇文章 0 订阅

normalizr 学习笔记 — node.js开发

最近开始学习node.js开发,有很多基础知识和框架需要学习,所以打算将自己学习笔记记录下来,即可以和大家分享,也可以方便自己以后回顾知识。由于只是个人笔记,便不是详细的教程,有不懂的代码还请自行百度。


主要模块

  • normalizr

介绍

该模块主要将获取的数据进一步格式化,在store创建一个虚拟数据库,数据通过id引用

代码段

由于我学习时是配合redux进行使用的,所以这里主要是在redux的基础上进行讲解的,redux的相关内容可参考Redux 学习笔记 — node.js开发,或者自行查找自动学习。

我使用了redux官方的例子real-world中的部分源码,大家可以配合这个学习

import api from '../api'
...
applyMiddleware(thunk, api, createLogger())

添加real-world的api中间组件

import { Schema, arrayOf, normalize } from 'normalizr'

在api中导入normalizr模块

const historySchema = new Schema('historys', {
  idAttribute: history => history.ContractID
})
const userScheme = new Schema('users', {
  idAttribute: user => user.UserID
})
const replySchema = new Schema('replys', {
  idAttribute: reply => reply.ReplyID
})

userScheme.define({
  history: historySchema
})

historySchema.define({
  reply: replySchema
})

数据结构定义,define()定义包含的schema

export const Schemas = {
  HISTORY: historySchema,
  HISTORY_ARRAY: arrayOf(historySchema),
  REPLY: replySchema,
  REPLY_ARRAY: arrayOf(replySchema),
  USER: userScheme,
  USER_ARRAY: arrayOf(userScheme)
}

输出数据结构模板,arrayOf()模板数组化

const API_ROOT = 'http://localhost/app/'
function callApi(endpoint, schema, method, form) {
  const fullUrl = (endpoint.indexOf(API_ROOT) === -1) ? API_ROOT + endpoint : endpoint

  let requestType;
  if (method === 'POST') {
    requestType = {
      method: method,
      body:form
    }
  } else if (method === 'GET') {
    requestType = null
  }

  return fetch(fullUrl, requestType)
    .then(response => {
      return response.json()
        .then(json =>
          ({ json, response })
        )
    })
    .then(({ json, response }) => {
      if (!response.ok) {
        // 回调规范的处理失败格式
        return Promise.reject(json)
      }

      if (json.code !== 200) {
        // 回调规范的处理失败格式
        return Promise.reject(json)
      }

      return Object.assign({},normalize(json.data, schema))
    })
}

根据action传过来的参数,进行不同的网络请求,请求成功后的数据通过normalize()进行转换

// action key
export const CALL_API = 'CALL_API'

export default store => next => action => {
  const callAPI = action[CALL_API]
  if (typeof callAPI === 'undefined') {
    return next(action)
  }

  let { endpoint, method, form } = callAPI
  const { schema, types } = callAPI

  if (typeof endpoint === 'function') {
    endpoint = endpoint(store.getState())
  }

  if (typeof endpoint !== 'string') {
    throw new Error('Specify a string endpoint URL.')
  }
  if (!schema) {
    throw new Error('Specify one of the exported Schemas.')
  }
  if (!Array.isArray(types) || types.length !== 3) {
    throw new Error('Expected an array of three action types.')
  }


  if (!types.every(type => typeof type === 'string')) {
    throw new Error('Expected action types to be strings.')
  }

  function actionWith(data) {
    const finalAction = Object.assign({}, action, data)
    delete finalAction[CALL_API]
    return finalAction
  }

  const [ requestType, successType, failureType ] = types
  next(actionWith({ type: requestType }))

  return callApi(endpoint, schema, method, form).then(
    response => next(actionWith({
      response,
      type: successType
    })),
    error => next(actionWith({
      type: failureType,
      error: error.message || 'Something bad happened'
    }))
  )
}

分为3个action请求阶段,requestType, successType,failureType,根据callApi()返回的结构进行异步调用,此时数据已经存入store中的虚拟数据库,之后将action传给真正的reduce将id数据存入

import _ from 'lodash'
...
function entities(state = {
  users: {},
  historys: {},
  replys: {}
}, action) {

  if (action.response && action.response.entities) {
    return _.merge({}, state, action.response.entities)
  }

  return state
}

这里用到lodash模块的merge,将所有请求获取到的数据和store虚拟数据库进行合并更新

const pagination = combineReducers({
  historyBySection: paginate({
    mapActionToKey: action => action.section,
    types: [
      ActionTypes.HISTORY_REQUEST,
      ActionTypes.HISTORY_SUCCESS,
      ActionTypes.HISTORY_FAILURE
    ]
  }),
  replyByHistory: paginate({
    mapActionToKey: action => action.historyId,
    types: [
      ActionTypes.REPLY_REQUEST,
      ActionTypes.REPLY_SUCCESS,
      ActionTypes.REPLY_FAILURE
    ]
  })
})

根据不同类型实现数据分页的接口,mapActionToKey作为类型指针,type接收的ActionTypes

import merge from 'lodash/merge'
import union from 'lodash/union'

// Creates a reducer managing pagination, given the action types to handle,
// and a function telling how to extract the key from an action.
export default function paginate({ types, mapActionToKey }) {
  if (!Array.isArray(types) || types.length !== 3) {
    throw new Error('Expected types to be an array of three elements.')
  }
  if (!types.every(t => typeof t === 'string')) {
    throw new Error('Expected types to be strings.')
  }
  if (typeof mapActionToKey !== 'function') {
    throw new Error('Expected mapActionToKey to be a function.')
  }

  const [ requestType, successType, failureType ] = types

  function updatePagination(state = {
    isFetching: false,
    ids: []
  }, action) {
    switch (action.type) {
      case requestType:
        return merge({}, state, {
          isFetching: true
        })
      case successType:
        return merge({}, state, {
          isFetching: false,
          ids: union(state.ids, action.response.result),
        })
      case failureType:
        return merge({}, state, {
          isFetching: false
        })
      default:
        return state
    }
  }

  return function updatePaginationByKey(state = {}, action) {
    switch (action.type) {
      case requestType:
      case successType:
      case failureType:
        const key = mapActionToKey(action)
        if (typeof key !== 'string') {
          throw new Error('Expected key to be a string.')
        }
        return merge({}, state, {
          [key]: updatePagination(state[key], action)
        })
      default:
        return state
    }
  }
}

实现数据分页方法,配合了normalizr

function mapStateToProps(state, ownProps) {

  const section = ownProps.section;

  const {
    pagination: { historyBySection },
    entities: { historys }
  } = state;

  const historyPagination = historyBySection[section] || { isFetching: true, ids: [] }
  const items = historyPagination.ids.map(id => historys[id])

  return {
    historyPagination,
    items
  }

}

根据分页指针和reduce中的id,从store虚拟数据库中获取真正的数据

部分代码来自于redux官方例子real-world

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值