Recently, I developed a React project. Since I was engaged in the original hybrid H5 development before, I was not familiar with the use of Redux, so I wanted to practice this time. Then I spent a few days looking at Redux and several construction schemes, as well as other H5 projects of the company (directly using Redux), and I felt it was very complicated. I have used the official version of Ant-Design-Pro 2.0 (added umi version) and found it very good, so I decided to build a project from UMI to do the H5 project of mobile terminal. After removing most of the business codes, I brought this demo to share with you, hoping to be of some value to novice UMI project construction.

Write this article is to help people in need of a better understanding of UMI to build the project, the first time to write the article shortcomings hope pointing out the example of demo in my github address can be downloaded reference github address feel on their own understanding and use UMI project helpful point star

## Create project

  • Umi provides scaffolding, you can refer to the umi website to create a project through scaffolding section
  • Package manager I use YARN

Yarn was introduced by Facebook and is recommended in some of the newer React books and materials. Yarn has the following advantages over NPM: fast speed, offline mode, and version control.

  • Too much nonsense on the code dry, under the empty folder
yarn create umi
Copy the code

A selection box will appear, I selected ANTD, DLL, hard Source. These several configurations in umi’s official website quick start inside the configuration explanation. Make sure node is 8.10 or later

  • Install dependencies
yarn
Copy the code
  • Start the project
yarn start
Copy the code
  • Compile the package
umi build
Copy the code

## My understanding of Umi

  1. In A DVA project it’s usually a separate onemodelsAnd then all of themmodelsWrite it inside. After using UMI, you can write a Models folder under Pages to manage all models. You can also write a Models folder under each page to store the models needed on the current page. The advantage is that the structure is clearer and it is easier to delete without deleting several places.Models are automatically registered
  2. Router.js has been removed from the contract route, which UMI will base onpagesThe js of the page under the directory automatically generates the route configuration. You can refer toRoute on the UMI official website.
  • Suppose the pages directory structure is as follows:
+ pages/
  + users/
    - index.js
    - index.less
  - index.js
Copy the code
  • Umi automatically generates the following route configurations:
[
  { path: '/', component: './pages/index.js' },
  { path: '/users/', component: './pages/users/index.js'},]Copy the code
  1. Good dynamic routing (good for transferring values and values between pages)
  • For example, to realize the jump from the list to the details of the order, it is generally necessary to bring an ID in the past, in the details page with ID to get some data, then in UMI how do we do?
+ pages/
  + orderdetail/
    - $id$.js
    - index.less
Copy the code
  • So when the list jumps to the orderDetail page above (let’s say val.id=15), remember to go fromimport router from 'umi/router'
router.push('/orderdetail/' + val.id)     
Copy the code
  • Get this ID from the OrderDetail page (you can print this for yourself).
this.props.match.params.id  
Copy the code
  1. With fewer configuration files, umi will have fewer dependencies in package.json, such as antD selected when creating projects
  • antd
  • antd-mobile
  • Babel – plugin – import # on the demo

The effect after running

Global Layout component

SRC /layouts/index.js is a global routing convention that returns a React component and renders child components via props. Children.

+ layouts/
  + baseLayout/
    - index.js
    - index.less
  - index.js
Copy the code
  • Take a look at the index.js directory
import React, { Component } from 'react'
import BaseLayout from './baseLayout'; // Bottom navigation component const ULR_NO_LAYOUT = ['/'.'/home'.'/class'.'/my']; Class Index extends Component {class Index extends Component {componentDidMount() {
  }
  renderBody = () => {
    const {location: {pathname}, children } = this.props;
    if (ULR_NO_LAYOUT.includes(pathname)) {
      return(<BaseLayout {... this.props} />); }return (
      <React.Fragment>
        {children}
      </React.Fragment>
    );
  }

  render() {
    return (
      <React.Fragment>
        {this.renderBody()}
      </React.Fragment>
    )
  }
}

export default Index;
Copy the code
  • ULR_NO_LAYOUTThe variable is to determine under which routes the bottom navigation should appear
  • BaseLayoutIs to useantd-mobileTabBar
The mock data

The convention is that all.js files in the mock directory will be resolved as mock files. Let’s create a new home.js

exportDefault {// The value can be Object or Array'GET /api/users': {users: [1, 2]}, // GET POST can be omitted'/api/users/1': {id: 1}, // Support custom functions, API see express@4'POST /api/users/create': (req, res) => { res.end('OK'); }};Copy the code

{users: [1, 2]}

The request file

You need to encapsulate the request based on your project and the way you receive it in the background

  • For example, lines 49 and 56 of the request are tokens added when I requested the interface for my project, which can be removed
// body Adds the tokenif (newOptions.body) {
    newOptions.body.__token__ = getToken();
  } else {
    newOptions.body = {
      __token__: getToken(),
    };
  }
Copy the code
  • For example, lines 86 and 89 of request add conversions to get passes,setUrlEncodedMethod, so that the get method can also pass through the object as the POST method, automatic conversion belt after the URL
} else if (newOptions.method === 'GET') {
    new_url = url + '? ' + setUrlEncoded(newOptions.body)
    delete newOptions.body
  }

//setUrlEncoded methodexport const setUrlEncoded = (obj) => {
    let urlEncoded = ' ';
    if(obj && obj instanceof Object) {
        const keys = Object.keys(obj);
        if(keys && keys.length) {
            keys.forEach((key, index) => {
                urlEncoded += `${key}=${obj[key]}`;
                if(index + 1 < keys.length){
                    urlEncoded += '&'; }}); }}return urlEncoded;
}
Copy the code
  • The 110 lines of the request need to be modified based on the return value of the itemresponse
Gets and validates the value of the form
  • It is recommended to userc-form
import { createForm } from 'rc-form';
@createForm()
Copy the code
  • Calls need to be made fromthis.propsGet in theformIn thegetFieldProps, getFieldError

render() { const {form: {getFieldProps, getFieldError}, regLoading} = this.props; <div> <InputItem {... getFieldProps('name', {
              initialValue: ' ',
              rules: [{
                  required: true,
                  message: 'Please enter a user name', }], })} clear error={!! getFieldError('name')}
            onErrorClick={() => {
              const err = getFieldError('name').join(', ');
              Toast.info(err, 1);
            }}
            placeholder='Username'
          />
    </div>
}
Copy the code
  • When submitting validation form input, for example
  submit(){
    const {form, dispatch} = this.props;
    form.validateFields((error, fieldsValue) => {
      if (error) {
        return;
      }
      dispatch({
        type: 'login/submit'Content: {/ / into}, the callback: (res) = > {/ / need to implement what}}); }); }Copy the code
Models calls and models files
  • For example, index.js at home is called
import { connect } from 'dva';
@connect(({ home }) => ({ home }))
Copy the code
  • The models folder home.js of home is usedsubscriptions.subscriptionsThe benefit should be that you can monitor global changes and make data changes or request interfaces in the Model even if they are not related to the current page. Of course, you can also choose to use it on the pagedispatch
import { reg } from 'services/home';
import router from 'umi/router';
export default {
  namespace: 'home',
  state: {
    'list': {'productList': ' '.'bannerList': ' '
    }
  },
  effects: {
    *reg({ payload, callback }, { call, put }) {
      const response = yield call(reg, payload);
      yield put({
        type: 'setData',
        payload: response.data
      });
    }
  },
  reducers: {
    setData(state, { payload }) {
      return {
        ...state,
        list: payload,
      }
    }
  },
  subscriptions: {
    setup({ dispatch, history{})return history.listen(({ pathname, search }) => {
        if (pathname == '/home'||pathname == '/') {
          dispatch({
            type: 'reg'}); }}); ,}}};Copy the code