Comments on DVA

Because we used react plus redux. This time, I got to dVA framework for a simple study. This article mainly explains the dVA framework development common API and some use tips, if you want to see more comprehensive API, please refer to the DVA official document: official Demo

Simple installation steps

1. Install DVA-CLI globally

$ npm install -g dva-cli
Copy the code

2. Then use DVa-cli to create our project folder, where mydva is the name of the project.

$ dva new mydva
Copy the code

3. Go to the mydva directory, install dependencies, and perform the following operations.

$ cd myapp
$ npm start
Copy the code

4. After the startup is successful, the following interface is displayed

5. File catalog and analysis

├─ SRC // Most important folder ├─ SRC // Most important folder ├─ SRC // Most important folder ├─ SRC // Most important folder │ ├─ Elements │ ├─ Elements │ ├─ Elements │ ├─ Elements │ ├─ Models │ ├─ Elements │ ├─ Elements │ ├─ Models │ ├─ Elements │ ├─ Elements │ ├─ Elements │ ├─ routes // React All the data interaction and logic is written here. │ ├ ─ ─ services / / put the request on the pretext of method folder │ ├ ─ ─ utils / / my own method of tool can be placed in side │ ├ ─ ─ index. The CSS / / entry document style │ ├ ─ ─ index. The ejs / / ejs template engine │ ├ ─ ─ Index. Js / / entry documents │ └ ─ ─ the router. The js / / project routing file ├ ─ ─ the eslintrc / / bower installation directory configuration ├ ─ ─ the editorconfig / / that code in different visual editor tool ├ ─ ─ .gitignore // ├─.roadhogrc.js // Project config file, config interface forwarding, cSS_module, etc. ├─.roadhogrc.mock. Js // Project config file ├─ package.json // Current whole project dependencyCopy the code

Simple demo, referenceOfficial computing demo

1. Fix it firstroute/IndexPage.js

import React from 'react';
import { connect } from 'dva';
import styles from './IndexPage.css';

class IndexPage extends React.Component {
  render() {
    const { dispatch } = this.props;

    return( <div className={styles.normal}> <div className={styles.record}>Highest Record: 1</div> <div className={styles.current}>2</div> <div className={styles.button}> <button onClick={() => {}}>+</button> </div> </div> ); }}export default connect()(IndexPage);
Copy the code

2. Second, modify the styleroutes/IndexPage.css

.normal {
  width: 200px;
  margin: 100px auto;
  padding: 20px;
  border: 1px solid #ccc;
  box-shadow: 0 0 20px #ccc;
}
.record {
  border-bottom: 1px solid #ccc;
  padding-bottom: 8px;
  color: #ccc;
}
.current {
  text-align: center;
  font-size: 40px;
  padding: 40px 0;
}
.button {
  text-align: center;
}
button {
  width: 100px;
  height: 40px;
  background: #aaa;
  color: #fff;
}
Copy the code

3. The following information is displayed

In 4.modelTake care of it inside.state, output in the pagemodelIn thestate

(1) First we are inindex.jsLt.models/example.jsOpen the comment for the next line of model.
import './index.css';

import dva from 'dva';
import model from './models/example'
import router from './router'// 1. Initialize creates dVA column const app = dva(); Plugins (optional) // app.use({}); // 3. Model Register modal app. Model (Model); // 4. Router Configureroute app. Router (Router); // 5. Start Start app.'#root');
Copy the code
(2) Next we entermodels/example.jsThat will benamespaceName tocount.stateObject plusrecordcurrentattribute
export default {

  namespace: 'count',

  state: {
    record: 0,
    current: 0,
  },

  subscriptions: {
    setup({ dispatch, history }) {  // eslint-disable-lines
    },
  },

  effects: {
    *fetch({ payload }, { call, put }) {  // eslint-disable-line
      yield put({ type: 'save' });
    },
  },

  reducers: {
    save(state, action) {
      return{... state, ... action.payload }; ,}}};Copy the code
(3) Then we cameroutes/indexpage.jsPage, passedmapStateToPropsIntroduce relevantstate.
import React from "react";
import { connect } from "dva";
import styles from "./IndexPage.css";

class IndexPage extends React.Component {
  render() {
    const { dispatch, count } = this.props;

    return( <div className={styles.normal}> <div className={styles.record}>Highest Record: <div className={styles. Current}>{count. Current}</div> <div className={styles.button}> <button onClick={() => { }} > + </button> </div> </div> ); }}function mapStateToProps(state) {
  return{ count: state.count }; } // get stateexport default connect(mapStateToProps)(IndexPage);
Copy the code
React dependency injection (DDI)mapStateToProps/mapDispatchToProps)

Inject the node of the required state into the component associated with this view data

function mapStateToProps(state, ownProps) {
    return {
            loading:state.getIn(['projectPre'.'projectMgr'.'loading']),
            data:state.getIn(['APP'.'data']),... } // Loading and data are from the corresponding reduce}Copy the code

Inject the response events that need to be bound into the component

function mapDispatchToProps(dispatch){
    return{... BindActionCreators (Action, dispatch)}} // mapDispatchToProps() functionbindActionCreators, Action need to introduce // import * as action from'.. /action';
    // import { bindActionCreators } from 'redux'; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / multiple action into the import * as the action of the from'.. /action';
import * as action2 from '.. /.. /.. /inde/action';
function mapDispatchToProps(dispatch){
    return{... bindActionCreators(action, dispatch) ... bindActionCreators(action2, Dispatch)}} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / introduce an action inside the multiple methods of import { activeOrAbandonedApproval, showSeller, getAddOrderDiscounts } from'.. /orderInfo/action'
function mapDispatchToProps(dispatch) {
  return {
    ...bindActionCreators({ activeOrAbandonedApproval, showSeller, getAddOrderDiscounts }, dispatch)
  }
}
Copy the code
(5)+sendactionThrough thereducerChange the correspondingstate
export default {
  ...
  reducers: {
    add1(state) {
      const newCurrent = state.current + 1;
      return { ...state,
        record: newCurrent > state.record ? newCurrent : state.record,
        current: newCurrent,
      };
    },
    minus(state) {
      return{... state, current: state.current - 1 }; ,}}};Copy the code
(6) First we are inmodels/example.jsWrite the correspondingreducer.
export default {
  namespace: "count",

  state: {
    record: 0,
    current: 0
  },

  subscriptions: {
    setup({ dispatch, history }) {
      // eslint-disable-lines
    }
  },

  effects: {
    *fetch({ payload }, { call, put }) {
      // eslint-disable-line
      yield put({ type: "save" });
    }
  },

  reducers: {
    add(state) {
      const newCurrent = state.current + 1;
      return {
        ...state,
        record: newCurrent > state.record ? newCurrent : state.record,
        current: newCurrent
      };
    },
    minus(state) {
      return{... state, current: state.current - 1 }; }}};Copy the code
(7) Template in the pageroutes/IndexPage.js+When the number clicks,dispatchaaction
import React from "react";
import { connect } from "dva";
import styles from "./IndexPage.css";

class IndexPage extends React.Component {
  componentDidMount() {
    console.log(this.props);
  }
  render() {
    const { dispatch, count } = this.props;

    return( <div className={styles.normal}> <div className={styles.record}>Highest Record: <div className={styles. Current}>{count. Current}</div> <div className={styles.button}> <button onClick={() => { dispatch({type: "count/add"}); }} > + </button> </div> </div> ); }}function mapStateToProps(state) {
  return{ count: state.count }; } // get stateexport default connect(mapStateToProps)(IndexPage);
Copy the code
The effect is as follows:

5. Let’s use iteffectSimulates a data interface request and returns throughyield put()Change the correspondingstate

(1) First let’s replace the correspondingmodels/example.jseffect
effects: {
    *add(action, { call, put }) {
      yield call(delay, 1000);
      yield put({ type: 'minus'}); }},Copy the code
(2) heredelay, a function of delay, we write inutilsI’m gonna write autils.jsGenerally, functions that request interfaces are written inserversFolder.
export function delay(timeout) {
  return new Promise((resolve) => {
    setTimeout(resolve, timeout);
  });
}
Copy the code
(3) thenmodels/example.jsTo import theutils.js
import { delay } from '.. /utils/utils';
Copy the code

6. Subscribe to subscribe to keyboard events, usesubscriptionsWhen the user holds downcommand+upTrigger to add numbersaction

(1) You need to install the keyMaster dependency
npm install keymaster --save
Copy the code
(2) inmodels/example.jsIn the following modifications, herewindowsIn theupIt’s the keyboardwrite.
import key from 'keymaster'; . app.model({ namespace:'count',
+  subscriptions: {
+    keyboardWatcher({ dispatch }) {
+      key('⌘ + up and CTRL + up', () => { dispatch({type:'add'})}); +}, +},});Copy the code

7. In the example we see when we keep clicking+After the button, we’ll seecurrentIt keeps incrementing by one, but after one second, it automatically decreases to zero. So how do we fix it?

(1) We should go to schooleffectTo send a message about addingactionBut we areeffectYou can’t write that directly in
effects: {
    *add(action, { call, put }) {
      yield put({ type: 'add' });
      yield call(delay, 1000);
      yield put({ type: 'minus'}); }},Copy the code
Because if I do,effectwithreducersIn theaddMethods overlap, and you get stuck in an infinite loop, because when the component sends adispatchThe time,modelThey go firsteffectThe way inside, when found againadd“, will ask againeffectThe method inside.
(2) Should be changedreducersInside the way to make it not witheffectThe same way willreducersIn theaddInstead ofadd1.
reducers: {
    add1(state) {
      const newCurrent = state.current + 1;
      return { ...state,
        record: newCurrent > state.record ? newCurrent : state.record,
        current: newCurrent,
      };
    },
    minus(state) {
      return { ...state, current: state.current - 1};
    },
  },
  effects: {
    *add(action, { call, put }) {
      yield put({ type: 'add1' });
      yield call(delay, 1000);
      yield put({ type: 'minus'}); }},Copy the code

conclusion

Summary of model about DVA framework

1.state

The concept of state is the same as before, except that it has a lower priority than the initialization, but the state in the project is basically defined here.

2.namespace

The namespace of Model, as well as its attributes on the global state, can only use strings. We need namespace when we send actions to the corresponding reducer.

3.Reducer

Reducer is defined in key/value format, which is used to handle synchronization operations and is the only place where state can be changed. Triggered by action. It’s actually a pure function.

4.Effect

It is used to process asynchronous operations and business logic. State is not directly modified. Simply speaking, it is a place to obtain data from the server and initiate an action to send to reducer.

It uses Redux-saga, which has several commonly used functions.

*add(action, { call, put }) {
    yield call(delay, 1000);
    yield put({ type: 'minus' });
},
Copy the code
5.Effects

(1) Put: triggers action

yield put({ type: ' ',payload:' ' });
Copy the code

(2) Call is used for asynchronous logic processing and supports Promise

const result= yield call(fetch,' ');
Copy the code

(3)select is used to obtain data by state

const todo = yield select(state=>state.todo);
Copy the code
6.Subscription

Subscription is a subscription that is used to subscribe to a data source and dispatch corresponding actions as needed. Executed when app.start(), the data source can be the current time, the CURRENT page URL, the server’s Websocket connection, the history route change, and so on.