Front knowledge

  • dva
  • roadhog
  • umi
  • Dva knowledge map
  • Use UMI to improve DVA project development

Umi + DVA to complete user management of CURD applications

This article is based on dVA author @sorrycc’s example of UMI + DVA, the CURD application that completed user management. In response to @sorrycc’s Github post in the comments section, yes, more than 90% of the sorrycc text in this post came from Umi + Dva, the user-managed CURD app, and the author and source were right in the beginning. The purpose of writing this post was to document myself following the step by step tutorial on the app. It’s not a tutorial, so expect @sorrycc to provide some rich sorrycc examples on the DVA website.

September 10, 2018 update, UMI has been upgraded to version 2.0, students who want to use umi@2 development please step to DvaJS learning road 2-umi@2 + dVA, complete user management CURD application.

Before you start:

  • Ensure that the Node version is 8.4 or later
  • Using CNPM or YARN can save you time installing dependencies

Step 1. Install dva-cli@next and create an application

Install dvA-CLI first and make sure the version is 1.0.0-beta.2 or above.

$CNPM I dva-cli@next -g $dva -v dva-cli version 1.0.0-beta.4 dva version 2.3.1Copy the code

It should be noted here that the reason for installing dva-cli@next version is that UMI has not provided the official scaffolding tool at present, and dVA + UMI can be used together with dva-cli@next to initialize the project. See: Examples and scaffolding.

Then create the application:

$ dva new user-dashboard
$ cd user-dashboard
Copy the code

Step 2. Configure a proxy that can be accessed in RESTFul modehttp://localhost:8000/api/users

Modify.umirc.js by adding “proxy” to the configuration:

proxy: {
  "/api": {
    "target": "http://jsonplaceholder.typicode.com/"."changeOrigin": true."pathRewrite": { "^/api" : ""}}},Copy the code

Then start the application :(this command remains on, no need to restart later)

$ npm start
Copy the code

The browser is automatically opened and http://localhost:8000 is displayed.

Visit http://localhost:8000/api/users, you can access data at the jsonplaceholder.typicode.com/users. Occasional failures may occur due to the stability of the Typicode.com service. But it doesn’t matter, it will help us deal with errors later.)

Step 3. Generate the Users route

The file in UMI is the route, so we need to add a route and create a new file, see umijs.org/guide/route… .

SRC /pages/users/page.js

export default () => {
  return (
    <div>
      Users Page
    </div>
  )
}
Copy the code

Then go to http://localhost:8000/users, you will see the Users Page output.

Note: Use the UMI convention that files in the SRC /pages directory are routes, and files are exported to the React component. You can see the characteristic of UMI: models and services are organized together in a page dimension.

Step 4. Construct the Users Model and service

The new service: SRC/pages/users/services/users. Js:

import request from '.. /.. /.. /utils/request';

export function fetch({ page = 1 }) {
  returnrequest(`/api/users? _page=${page}&_limit=5`);
}
Copy the code

Note that the page parameter defaults to 1 and the LIMIT parameter is set to 5

New model: SRC/pages/users/models/users, js, content is as follows:

import * as usersService from '.. /services/users';

export default {
  namespace: 'users',
  state: {
    list: [],
    total: null,
  },
  reducers: {
    save(state, { payload: { data: list, total } }) {
      return { ...state, list, total };
    },
  },
  effects: {
    *fetch({ payload: { page } }, { call, put }) {
      const { data, headers } = yield call(usersService.fetch, { page });
      yield put({ type: 'save', payload: { data, total: headers['x-total-count']}}); }, }, subscriptions: { setup({ dispatch,history{})return history.listen(({ pathname, query }) => {
        if (pathname === '/users') {
          dispatch({ type: 'fetch', payload: query }); }}); ,}}};Copy the code

Payload: {data: list, total}} {payload: {data: list, total}} return { … State, list, total} = Spread Operator… To group new objects, see dVA Knowledge map #ES6 Objects and Arrays

SRC /utils/request.js:

import fetch from 'dva/fetch';

function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response;
  }

  const error = new Error(response.statusText);
  error.response = response;
  throw error;
}

/**
 * Requests a URL, returning a promise.
 *
 * @param  {string} url       The URL we want to request
 * @param  {object} [options] The options we want to pass to "fetch"
 * @return {object}           An object containing either "data" or "err"* /export default async function request(url, options) {
  const response = await fetch(url, options);

  checkStatus(response);

  const data = await response.json();

  const ret = {
    data,
    headers: {},
  };

  if (response.headers.get('x-total-count')) {
    ret.headers['x-total-count'] = response.headers.get('x-total-count');
  }

  return ret;
}
Copy the code

Note: the async/await feature of ES7 is used here. I am not familiar with this area at first. I have read some articles about async/await, and I find that Promise is more semantically written than Promise.

/**
 * Requests a URL, returning a promise.
 *
 * @param  {string} url       The URL we want to request
 * @param  {object} [options] The options we want to pass to "fetch"
 * @return {object}           An object containing either "data" or "err"* /export default function request(url, options) {
  let headers = {}
  return fetch(url, options)
    .then(checkStatus)
    .then(response => {
      const data = parseJSON(response)
      if (response.headers.get('x-total-count')) {
        headers['x-total-count'] = response.headers.get('x-total-count');
      }
      return data;
    }).then((data) => {
      return {data, headers}
    }).catch(err => ({ err }));
}
Copy the code

If you switch to the browser (which will refresh automatically), nothing should change because the data is ready but there is no view associated with it. But if you open the Redux developer tool, you should see the Users/Fetch and Users/Save actions and their associated states.

Step 5. Add an interface to display the list of users

We put the component in the SRC/pages/users/components, so new users here. Js and users. The CSS. Refer to this Commit for details.

Two things to note:

  1. The Model was fine-tuned to add page to represent the current page
  2. Since pageSize is used in both Components and services, extract thesrc/constants.js

When you’re done, switch to your browser, and you should see a paging list of users.

A few points to note:

  • Antd component is used in users. js, but antD was not installed manually in the project. Originally, UMI helped us to introduce ANTD.
  • The users.js module connects the model to the componentconst { list, total, page } = state.users;The inside of theusersmodelThe inside of thenamespaceThe name.
  • We did not manually register the model. Umi helped us with this step. See detailssrc/pages/.umi/DvaContainer.jsFile that will be updated automatically. For related rules, seeUmi official website # Model registrationSection.
  • You can use the CSS Module directly

Step 6. Add layout

Add a layout that allows you to switch back and forth between the home page and the user list page. In UMI, the layout /index.js protocol is global, so we are about to install the SRC /layouts/index.js and CSS files.

Refer to this Commit.

Note:

The menu in the header changes as the page switches, highlighting the menu item in which the current page is located

Step 7. Handle the loading state

Dva has a hook to manage effects execution and encapsulates the DVA-loading plug-in based on it. With this plugin, we don’t have to write showLoading and hideLoading over and over again. When a request is made, the plugin automatically sets the loading state of the data to true or False. We then bind and render from this data when we render the Components.

Umi-plugin-dva has a built-in DVA-loading plug-in by default.

Then in the SRC/components/Users/Users. Binding loading data in js:

+ loading: state.loading.models.users,
Copy the code

Refer to this Commit for details.

Refresh the browser. Is your user list loading?

Step 8. Handle paging

Only change to a file SRC/pages/users/components/users. Js.

There are two ideas for dealing with paging:

  1. Send an action, request new paging data, save it to the Model, and then automatically update the page
  2. Switching routes (since we listened for route changes, everything else will be handled automatically)

We used the Idea 2 approach, which has the advantage that users can go directly to Page 2 or other pages.

Refer to this Commit.

Step 9. Delete the user

After the previous 8 steps, the overall context of the application has been clear, I believe you have a certain understanding of the overall process.

The following functions can be adjusted in the following three steps:

  1. service

  2. model

  3. Component we are now starting to add user removal.

  4. Service, modify SRC/pages/users/services/users. Js:

export function remove(id) {
  return request(`/api/users/${id}`, {
    method: 'DELETE'}); }Copy the code
  1. The model, the modifiedsrc/pages/users/models/users.js:
*remove({ payload: id }, { call, put, select }) {
  yield call(usersService.remove, id);
  const page = yield select(state => state.users.page);
  yield put({ type: 'fetch', payload: { page } });
},
Copy the code
  1. Component, modifysrc/pages/users/components/Users.jsTo replacedeleteHandlerContent:
dispatch({
  type: 'users/remove',
  payload: id,
});
Copy the code

Switch to the browser and the delete function should be in effect.

Step 10. Handle user editing

Handling user editing follows the same three steps as before:

  1. service
  2. model
  3. Component first service, modifysrc/pages/users/services/users.js:
export function patch(id, values) {
  return request(`/api/users/${id}`, {
    method: 'PATCH',
    body: JSON.stringify(values),
  });
}
Copy the code

Is the model again, modify SRC/pages/users/models/users. Js:

*patch({ payload: { id, values } }, { call, put, select }) {
  yield call(usersService.patch, id, values);
  const page = yield select(state => state.users.page);
  yield put({ type: 'fetch', payload: { page } });
},
Copy the code

Finally, component. See Commit.

One thing to note here is that we have several options for dealing with Modal visible states:

  1. In the MODEL state of DVA
  2. Storage component in the state

In addition, how to save is also a problem, can:

  1. There is only one visible, and then fill in the form data depending on which user the user clicks on
  2. This tutorial uses scenario 2-2, where the Component state is saved and user is saved for visible. In addition, for the convenience of use, a packageUserModalThe component.

When you’re done, switch to your browser and you should be able to edit the user.

Step 11. Process user creation

User creation is simpler than user editing because UserModal components can be shared. This is similar to Step 10, so see Commit.


At this point, we have a complete CURD application. If you are interested, you can further check the information of DVA and UMI:

  • Dva website
  • Umi website

(after)

conclusion

Do this exercise to understand how to use DVA and UMI together, some ways to write umi and the characteristics of UMI. I feel that after learning Redux, DVA will get started quickly, DVA website resources are very rich, hope to learn with you, there will be dVA learning articles.