dva 与 redux-saga应用

dva是什么?

  • dva是一个基于Redux与React-saga的数据流解决方案。

  • 为了简化开发流程,另外内置React-Router与Redux。

如何使用?

  • 下载dva-cli

    // 第一步
    npm install dva-cli -g
    dva -v  // 查版本 确认安装成功
    // 第二步
    dva new your-project
    cd your-project
    npm run start 
  • dva提供的API

    import dva from 'dva'

    const app = dva({
        //指定给路由用的 history,默认是 hashHistory
        history, 
        //初始化Model中state状态,比Model优先高, 默认 {}
        initialState, 
        // 对应功能钩子函数 
        onError,
        onAction,
        onStateChange,
        onReducer,
        onEffect,
        onHmr,
        extraReducers,
        extraEnhancers,
    })
    // 执行插件
    app.use() 
    // 存储纯函数 修改数据的方法对象
    app.model( required('..model/example.js').defaut )
    // 路由配置文件,可对路由表以JavaScript对象的形式去配置, 如不要路由可直接返回组件,
    app.router( requried('../router').default)
    // 挂载启动
    app.start('element');

  • Model是一个JavaScript对象, 它包含如下


export default {
    // 每个Model 唯一的Key, model 外提交action 需添加 key 例如 example/add
    namespace: 'example',
    // 初始化状态 
    state: 0, 
    // 监听
    subscriptions: { 
        // 函数名可以随便起
        keyEvent({dispatch}){
            window.onresize = function(){
                dispatch({type: 'add'})
            }
        }
    },
    // 纯函树更新状态,触发视图更新
    reducers: { 
        add({example}, action){
            return {
                ...example,
                count: example.count+1
            }
        },
        reduce({example}, action){
            return {
                ...example,
                count: example.count+1
            }
        }
    },
    // 不是纯函数,如提交的异步action 走这里
    effects: {
        *asyncName({ars}, {call, put, select}){
            // 执行异步操作;
            yield call() 
            // Get state 
            yield select(({example}))
            // 提交action
            yield put({type: 'add'}) 
        }
    }

}

  • React Component 如何提交Action


import React from 'react';
import { connect } from 'dva'


class Example extends React.Component {
    render(){
        let {count, dispatch } = this.props;
        return (
            <div>
                <p>{count}</p>
                <button onClick={()=> dispatch({type: 'example/add'}) }>增加</button>
                <button onClick={()=> dispatch({type: 'example/reduce'}) }>减少</button>
            </div>
        )
    }
}
//  组件与dva model 绑定 
const mapStateToprops = ({example}) =>{
    return {...example, count: example.count}
};

export default connect(mapStateToprops)(Example);

Ant-Design-Pro 项目目录结构

Ant-Design-Pro 与 dva 结合使用

  • Router篇


// 通过JavaScript 对配置路由组件信息
const routerConfig = {
    // hideInBreadcrumb: true,
    // name: '工作台',
    // authority: 'admin',
    '/': {
      component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),
    },
    '/dashboard/analysis': {
      component: dynamicWrapper(app, ['chart'], () => import('../routes/Dashboard/Analysis')),
    },
    '/dashboard/monitor': {
      component: dynamicWrapper(app, ['monitor'], () => import('../routes/Dashboard/Monitor')),
    },
    '/dashboard/workplace': {
      component: dynamicWrapper(app, ['project', 'activities', 'chart'], () =>
        import('../routes/Dashboard/Workplace')
      ),
    }
};
     

  • Model篇 effects 与 reducers 的应用

// routes/user/Login.js  登录
handleSubmit = (err, values) => {
    const { type } = this.state;
    const { dispatch } = this.props;
    if (!err) {
        dispatch({
            // model外提交action需要加model key 例如下方
            type: 'login/login', 
            payload: {
                ...values,
                type,
            },
        });
    }
};
// layouts/BasicLayout.js   退出
  handleMenuClick = ({ key }) => {
    const { dispatch } = this.props;
    if (key === 'triggerError') {
      dispatch(routerRedux.push('/exception/trigger'));
      return;
    }
    if (key === 'logout') {
      dispatch({
        type: 'login/logout',
      });
    }
  };

// models/login.js
export default {
    namescape: 'login',
    status: undefined,
    effects: {
        // 登录 
        *login({ payload }, { call, put }) {
            // fakeAccountLogin 异步函数 
            const response = yield call(fakeAccountLogin, payload);
            yield put({
                type: 'changeLoginStatus',
                payload: response,
            });
            // .......
        },
        // 退出
        *logout(_, { put }) {
            yield put({
                type: 'changeLoginStatus',
                payload: {
                    status: false,
                    currentAuthority: 'guest',
                },
            });
            reloadAuthorized();
             // 切换路由到 登录页
            yield put(
                routerRedux.push({
                    pathname: '/user/login',
                    search: stringify({
                        redirect: window.location.href,
                    }),
                })
            );
        },
    },
    reducers: {
        // 更改状态,触发视图更新
        changeLoginStatus(state, { payload }) {
            setAuthority(payload.currentAuthority);
            return {
            ...state,
            status: payload.status,
            type: payload.type,
        };
    },
  },
}
  • Model篇 subscriptions 的应用

// 监听路由的改变
 subscriptions: {
    setup({ history }) {
      // Subscribe history(url) change, trigger `load` action if pathname is `/`
      return history.listen(({ pathname, search }) => {
        if (typeof window.ga !== 'undefined') {
          window.ga('send', 'pageview', pathname + search);
        }
      });
    },
    keyEvent({ diapatch }) {
        // 监听窗口变化 去改变状态, 监听 表单输入等等。
        window.onresize = function(){
            diapatch({type: 'xx'})
        }
    },
  },

Model篇 effects 的应用

  • call 与 apply 执行异步操作

      // 执行异步操作
     function* asyncFn(){
          yield call(fn, args) 
          yield apply(fn, [args])
    
          // 如果要更改fn的this   
          yield call([this,fn], args)  
          yield apply([this,fn], [args])
     }
    
  • takeEvery 与t akeLatest <作用:监听action>

    // 如事件提交了多个action, 都会保留
     function* asyncFn(){
          // 如事件提交了多个action, 都会保留
          yield takeEvery('监听的aciton', fn) 
    
          // 如事件提交了多个action 只保留最后执行 那个
          yield takeLatest('监听action', fn)
     }
    
  • put <提交action, 通过reducers 改变状态, 渲染视图>

 function* asyncFn(){
        
        yield put({type: 'xxxx', args }) 
   }
  • select 获取module中的state

 function* asyncFn(){
        
        yield select()
   }
  • put 并发请求应用场景使用,effects结合axios.all()

function getUserAccount() {
    return axios.get('/toutiao/index?type=top&key=d2340952fcc9f969b9b920dca141f4fc');
}
  
function getUserPermissions() {
    return axios.get('/wepiao/query?key=f0c96cee32a57753af183345cded5716');
}

function httpAsync(){
    return axios.all([getUserAccount(), getUserPermissions()])
    .then(axios.spread(function (a,b) {
        let params = {a,b}
       return new Promise((reslove, reject)=> reslove(params));
    }));
  
}

export function* incrementAsync() { 
    let data = yield call([obj, httpAsync])
    yield put({ type: 'INCREMENT' })
    
}

// Our watcher Saga: 在每个 INCREMENT_ASYNC action spawn 一个新的 incrementAsync 任务
export function* watchIncrementAsync() {
    yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}

总结

  • Redux 相比 dva, 用dva 文件关联数减少, 节约了文件管理成本,切换成本,代码更简洁。
  • dva 集成了 Redux ,React/Router, React/saga ,dva/fetch, 不要在下载这些包了
  • router 除了可以组件的形式写, 也可通过 JavaScript 对象嵌套配置路由信息

你可能感兴趣的:(React)