基于React+Router+Redux+Sagas+fetch+ant Design +less + es6+mockjs的项目过程总结

app.js是首页面,路由放此。

sagas在此页面被引用。

import rootSaga from './sagas'

const store = configureStore(initialState)

store.runSaga(rootSaga);


1)FL.js

修改对应的接口Path值,便于后期对接口地址的管理。

SupSearch:'/api/Order/supSearch.json',
SupState:'/api/Order/supState.json',

2)sagas

中间件,只会在应用启动时调用(但初始启动的sagas可能会动态的调用其它sagas),用于监听发起的action,然后决定这个action用来做什么。

如:1.发起一个异步调用(ajax请求) 2.发起其它的action到store。 3.调用其它的sagas

index.js里面放所有的sagas。

import { watchGetAccountSaga } from './account'
import {supSearch} from './supSearch'
export default function* rootSaga() {
    yield [
        // 更多
        watchGetAccountSaga(),
        supSearch(),
    ]
}

子saga:有两个Generator函数。其中一个通过while(true),take方法来一直监听action。

只有当此事件被dispatch则fork第二个Generator函数。(此步骤避免了同时发起多个不必要的请求。

只有当view页面componentWillMount 才触发此action,发起请求。)

componentWillMount(){  
        this.props.initialDispatch()
    },
import { SUPSEARCHACTION, supSearchSucceed,supSearchFailed} from '../actions/sup'
export function* supSearch() {
    while (true) {
        yield take(SUPSEARCHACTION)
        yield fork(fetchSupSearchApi)
    }
}

第二个Generator方法主要用来生成fetch请求。

任务要被beginTask和endTask包裹。在请求成功之后,将请求到的数据dispatch一个对应的action(成功:supSearchSucceed(res) ) 失败(supSearchFailed(e) )。

function* fetchSupSearchApi(){
    try {
        yield put( beginTask() )
        const response = yield call(fetch, FL.PATH.API.SupSearch)
        const res = yield response.json()
        if (!res) {
            return false
        } else {
            yield put( supSearchSucceed(res) )   (成功)
        }
    } catch (e) {
        yield put( supSearchFailed(e) )   (失败)
    } finally {
        yield put( endTask() )
    }
}

mockjs也是放在此文件中。

// mock 代码放在 __DEBUG__ 代码块内,生产环境会删除代码块

if (__DEBUG__) {
    let data = Mock.mock({
        'Status': '200',
        'Position': '001',
        'ErrorCode': '001',
        'Data|1-10': [{
            'productNumber': /[A-Z][A-Z]\d{3}[a-z]\d{3}/,
            'productName': '@ctitle(3, 5)',
            'supplierName': '@ctitle(3)',
            'productStockStatus|1-100': 1,
            'faceValue|1-1000': 1,
            'productPrice|1-100': 1,
            'type|1-4': 1,
            'averageChargeTime':'@natural(0,200)'+'min',
            'complaintRate':'@natural(0,100)'+'%',
            'successRate': '@natural(0,100)'+'%',
            'today|1-100': 1,
            'week|1-1000':1 ,
            'month|1-10000': 1,
            'purchaseApplyStatus': '@boolean()'
        }]
    });
 
    fetchMock.mock(FL.PATH.API.SupSearch, data);
    // fetchMock.mock(FL.PATH.API.ACCOUNT, 404);
}

3)action

在对应的action文件sup.js中,

export const SUPSEARCHACTION = 'SUPSEARCHACTION'
export const SUPSEARCH_SUCCEED = 'SUPSEARCH_SUCCEED'
export const SUPSEARCH_FAILED = 'SUPSEARCH_FAILED'

分别对应触发,请求成功、请求失败的action:

export function supSearchAction(){
    return {
        type:SUPSEARCHACTION
    }
}
export function supSearchSucceed(items){
    return {
        type:SUPSEARCH_SUCCEED,
        items
    }
}
export function supSearchFailed(error) {
    return {
        type: SUPSEARCH_FAILED,
        error,
    }
}

4)reducer

index.js中为总reducer。通过import将各个reducer引入进来,通过combineReducer将各个子reducer结合起来。configureStore.js中引入此rootReducer。

import supSearch from './supSearch'
import supState from './supState'
 
 
const rootReducer = combineReducers({
    routing: routerReducer,
    supSearch,
    supState,
})
 
export default rootReducer

将action的三种type值引入,触发不同的type值会有不同的处理。

1、当触发SUPSEARCHACTION时,将isfetching设为true,代表开始请求数据。

2、当触发SUPSEARCH_SUCCEED时,将isfetching设为false,代表请求完成,成功,并将请求到的参数返回给items,items的值同步到state当中。

3、当触发SUPSEARCH_FAILED时,将isfetching设为false,代表请求完成,失败。返回错误。

import {SUPSEARCHACTION,SUPSEARCH_SUCCEED,SUPSEARCH_FAILED} from '../actions/sup'
 
export default function supSearch (state = {}, action) {
    switch (action.type) {
        case SUPSEARCHACTION:
            return {
                ...state,
                isfetching:true,
            }
        case SUPSEARCH_SUCCEED:
            return {
                ...state,
                isfetch:false,
                items: action.items,
            }
        case SUPSEARCH_FAILED:
            return {
                ...state,
                isfetching: false,
                error: action.error,
                };
        default:
            return state
    }
}
 

5)containers

用于将state进行处理,处理好的数据通过connect传递给component中的view页面 涉及到reselect(创建选择器)。

import { connect} from 'react-redux'  
import SupSearch from '../components/SupSearch/js/SupSearch'
import {supSearchAction} from '../actions/sup' 
import {createSelector } from 'reselect'
 
const defaultValue = [];
const stateItemSelector = (state) =>state.supSearch.items || defaultValue
const stateSelector = createSelector (
    [stateItemSelector],
    (stateItem) => {
        return stateItem
    }
)
const mapStateToProps = (state) =>{
    return {
        items:stateSelector(state),
    }
}
const mapDispatchToProps = (dispatch) =>{
    return {
        initialDispatch(){
            dispatch(supSearchAction())
        },
    }
}
export default connect(
    mapStateToProps,
    mapDispatchToProps
)(SupSearch)

6)component

此部分即为view模块。只有当组件即将被渲染时,才发起action,触发saga部分的yield take(SUPSEARCHACTION)

initialDispatch方法即为5)中mapDispatchToProps传过来的方法。

componentWillMount(){
        this.props.initialDispatch()
    },