react+dva+antd的騷操作

這裏寫圖片描述

原文鏈接

前言:

原諒我直接跳過react的基礎,直接就講react+dva的實際應用,因爲直接從項目結構來講,我覺得學習的成本更低,開發的速度更快,當然有時間的話還是建議衝react的基礎學起。

react的參考資料:
建議先從React 入門實例教程開始學
react的全家桶系列也不錯

dva的參考資料:
dva的官方指南有很多不錯的腳手架
ant design組合ant design UI可以說非常方便

例子
實現效果
首頁
修改頁面

前提條件
- 確保 node 版本是 6.5 +
- 用 cnpm 或 yarn 能節約你安裝依賴的時間

第一步:

打開cmd,切換到你要安裝的目錄,使用npm安裝dva-cli

$ npm i dva-cli@0.7 -g
$ dva -v
0.7.0

然後可以在命令行輸入命令創建應用

$ dva new user-dashboard
$ cd user-dashboard 

第二步,

配置 antd 和 babel-plugin-import
babel-plugin-import 主要是用於按需引入 antd 的 JavaScript 和 CSS,這樣打包出來的文件不至於太大。詳情請看dva

$ npm i antd --save
$ npm i babel-plugin-import --save-dev

如果dva的版本是在0.7一下的話是沒有.roadhogrc文件,修改 .roadhogrc,在 “extraBabelPlugins” 里加上,0.7一下的話是修改.webpackrc文件:

["import", { "libraryName": "antd", "style": "css" }]

第三步,

配置代理,能通過 RESTFul 的方式訪問
修改 .roadhogrc,加上 “proxy” 配置,或者修改.webpackrc文件也可:
可以通過http://localhost:8000/api/users來查看json數據

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

然後啓動應用:可以新開一個命令行窗口

$ npm start

然後系統就會自動在瀏覽器中打開頁面,也可以訪問http://localhost:8000/
看到這個圖片就可以下一步了

第四步,

生成 users 路由

dva g route users

輸入這個命令就會在src的routes生成兩個文件,一個User.js,一個是User.css,然後訪問 http://localhost:8000/#/users

第五步,

構造 users model 和 service
同上,用dva的命令生成文件

dva g model users

然後修改 src/models/users.js :

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 });
        }
      });
    },
  },
};

在目標文件夾中新增 src/services/users.js:

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

export function fetch({ page = 1 }) {
  return request(`/api/users?_page=${page}&_limit=5`);
}

由於我們需要從 response headers 中獲取 total users 數量,所以需要改造下 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;
}

第六步,

添加界面,讓用戶列表展現出來
用 dva-cli 生成 component:

$ dva g component Users/Users

然後修改生成出來的 src/components/Users/Users.js 和 src/components/Users/Users.css,並在 src/routes/Users.js 中引用他。
User.js

import React from 'react';
import { connect } from 'dva';
import { Table, Pagination, Popconfirm, Button } from 'antd';
import { routerRedux } from 'dva/router';
import styles from './Users.css';
import { PAGE_SIZE } from '../../constants';
import UserModal from './UserModal';

function Users({ dispatch, list: dataSource, loading, total, page: current }) {

  function deleteHandler(id) {
    dispatch({
      type: 'users/remove',
      payload: id,
    });
  }

  function editHandler(id, values) {
        dispatch({
            type: 'users/patch',
            payload: { id, values },
        });
      }

  function pageChangeHandler(page) {
    dispatch(routerRedux.push({
    pathname: '/users',
    query: { page },
     }));
  }

  function createHandler(values) {
        dispatch({
            type: 'users/create',
            payload: values,
          });
     }

  const columns = [
    {
      title: '姓名',
      dataIndex: 'name',
      key: 'name',
      render: text => <a href="">{text}</a>,
    },
    {
      title: '郵箱',
      dataIndex: 'email',
      key: 'email',
    },
    {
      title: '地址',
      dataIndex: 'website',
      key: 'website',
    },
    {
      title: '操作',
      key: 'operation',
      render: (text, record) => (
        <span className={styles.operation}>
            <UserModal record={record} onOk={editHandler.bind(null, record.id)}>
            <a>修改</a>
              </UserModal>
            <Popconfirm title="Confirm to delete?" onConfirm={deleteHandler.bind(null, record.id)}>
            <a href="">刪除</a>
          </Popconfirm>
        </span>
      ),
    },
  ];

  return (
    <div className={styles.normal}>
      <div>
          <div className={styles.create}>
            <UserModal record={{}} onOk={createHandler}>
                <Button type="primary">增加用戶</Button>
            </UserModal>
          </div>
        <Table
          columns={columns}
          dataSource={dataSource}
          loading={loading}
          rowKey={record => record.id}
          pagination={false}
        />
        <Pagination
          className="ant-table-pagination"
          total={total}
          current={current}
          pageSize={PAGE_SIZE}
          onChange={pageChangeHandler}
        />
      </div>
    </div>
  );
}

function mapStateToProps(state) {
  const { list, total, page } = state.users;
  return {
    loading: state.loading.models.users,
    list,
    total,
    page,
  };
}

export default connect(mapStateToProps)(Users);

User.css

.normal {
}
.create {
  margin-bottom: 1.5em;
}

.operation a {
  margin: 0 .5em;
}

這邊主要是對 model 進行了微調,加入了 page 表示當前頁
由於 components 和 services 中都用到了 pageSize,所以提取到 src/constants.js
改完後,切換到瀏覽器,應該能看到帶分頁的用戶列表

第七步,

添加 layout

  1. 添加 layout 佈局,使得我們可以在首頁和用戶列表頁之間來回切換。
    2.添加布局,src/components/MainLayout/MainLayout.js 和 CSS 文件
    在 src/routes 文件夾下的文件中引用這個佈局
import React from 'react';
import { Router, Route } from 'dva/router';
import IndexPage from './routes/IndexPage';

import Users from "./routes/Users.js";

function RouterConfig({ history }) {
  return (
    <Router history={history}>
      <Route path="/" component={IndexPage} />
      <Route path="/users" component={Users} />
    </Router>
  );
}

export default RouterConfig;

第八步,

通過 dva-loading 處理 loading 狀態
dva 有一個管理 effects 執行的 hook,並基於此封裝了 dva-loading 插件。通過這個插件,我們可以不必一遍遍地寫 showLoading 和 hideLoading,當發起請求時,插件會自動設置數據裏的 loading 狀態爲 true 或 false 。然後我們在渲染 components 時綁定並根據這個數據進行渲染。

先安裝 dva-loading :

npm i dva-loading --save

修改 src/index.js 加載插件,在對應的地方加入下面兩句:

import createLoading from 'dva-loading';
app.use(createLoading());

然後在 src/components/Users/Users.js 裏綁定 loading 數據:

loading: state.loading.models.users,

具體參考這個 例子

第九步,

處理分頁
只改一個文件 src/components/Users/Users.js 就好。

處理分頁有兩個思路:
1. 發 action,請求新的分頁數據,保存到 model,然後自動更新頁面
2. 切換路由 (由於之前監聽了路由變化,所以後續的事情會自動處理)
我們用的是思路 2 的方式,好處是用戶可以直接訪問到 page 2 或其他頁面。

參考這個 例子

第10步,

用戶的修改,刪除,增加都是對三個文件的操作

  1. service, 修改 src/services/users.js:
import request from '../utils/request';
import { PAGE_SIZE } from '../constants';

export function fetch({ page }) {
  return request(`/api/users?_page=${page}&_limit=${PAGE_SIZE}`);
 }
 //刪除
export function remove(id) {
  return request(`/api/users/${id}`, {
    method: 'DELETE',
  });
}
//修改
export function patch(id, values) {
  return request(`/api/users/${id}`, {
    method: 'PATCH',
    body: JSON.stringify(values),
    headers: {
    'Content-Type': 'application/json',
      'Accept': 'application/json',
  },
  });
}
//新增
export function create(values) {
   return request('/api/users', {
       method: 'POST',
        body: JSON.stringify(values),
      });
  }
  1. model, 修改 src/models/users.js:
import * as usersService from '../services/users';

export default {
  namespace: 'users',
  state: {
    list: [],
    total: null,
    page: null,
  },
  reducers: {
    save(state, { payload: { data: list, total, page } }) {
      return { ...state, list, total, page };
    },
  },
  effects: {
      //分頁
    *fetch({ payload: { page = 1 } }, { call, put }) {
      const { data, headers } = yield call(usersService.fetch, { page });
      yield put({
        type: 'save',
        payload: {
          data,
          total: parseInt(headers['x-total-count'], 10),
          page: parseInt(page, 10),
        },
      });
    },
    //刪除
  *remove({ payload: id }, { call, put }) {
      yield call(usersService.remove, id);
       yield put({ type: 'reload' });
  },
  //修改
*patch({ payload: { id, values } }, { call, put }) {
    yield call(usersService.patch, id, values);
    yield put({ type: 'reload' });
},
//新增
*create({ payload: values }, { call, put }) {
        yield call(usersService.create, values);
       yield put({ type: 'reload' });
      },
  *reload(action, { put, select }) {
      const page = yield select(state => state.users.page);
       yield put({ type: 'fetch', payload: { page } });
  },

  },
  subscriptions: {
    setup({ dispatch, history }) {
      return history.listen(({ pathname, query }) => {
        if (pathname === '/users') {
          dispatch({ type: 'fetch', payload: query });
        }
      });
    },
  },
};
  1. component, 修改 src/components/Users/Users.js,替換 deleteHandler 內容:
import React from 'react';
import { connect } from 'dva';
import { Table, Pagination, Popconfirm, Button } from 'antd';
import { routerRedux } from 'dva/router';
import styles from './Users.css';
import { PAGE_SIZE } from '../../constants';
import UserModal from './UserModal';

function Users({ dispatch, list: dataSource, loading, total, page: current }) {
//刪除
  function deleteHandler(id) {
    dispatch({
      type: 'users/remove',
      payload: id,
    });
  }
//修改
  function editHandler(id, values) {
        dispatch({
            type: 'users/patch',
            payload: { id, values },
        });
      }
//分頁
  function pageChangeHandler(page) {
    dispatch(routerRedux.push({
    pathname: '/users',
    query: { page },
     }));
  }
//新增
  function createHandler(values) {
        dispatch({
            type: 'users/create',
            payload: values,
          });
     }

  const columns = [
    {
      title: '姓名',
      dataIndex: 'name',
      key: 'name',
      render: text => <a href="">{text}</a>,
    },
    {
      title: '郵箱',
      dataIndex: 'email',
      key: 'email',
    },
    {
      title: '地址',
      dataIndex: 'website',
      key: 'website',
    },
    {
      title: '操作',
      key: 'operation',
      render: (text, record) => (
        <span className={styles.operation}>
            <UserModal record={record} onOk={editHandler.bind(null, record.id)}>
            <a>修改</a>
              </UserModal>
            <Popconfirm title="Confirm to delete?" onConfirm={deleteHandler.bind(null, record.id)}>
            <a href="">刪除</a>
          </Popconfirm>
        </span>
      ),
    },
  ];

  return (
    <div className={styles.normal}>
      <div>
          <div className={styles.create}>
            <UserModal record={{}} onOk={createHandler}>
                <Button type="primary">增加用戶</Button>
            </UserModal>
          </div>
        <Table
          columns={columns}
          dataSource={dataSource}
          loading={loading}
          rowKey={record => record.id}
          pagination={false}
        />
        <Pagination
          className="ant-table-pagination"
          total={total}
          current={current}
          pageSize={PAGE_SIZE}
          onChange={pageChangeHandler}
        />
      </div>
    </div>
  );
}

function mapStateToProps(state) {
  const { list, total, page } = state.users;
  return {
    loading: state.loading.models.users,
    list,
    total,
    page,
  };
}

export default connect(mapStateToProps)(Users);

以上就是react+dva+antd實現的一個簡單的增刪改的操作例子。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章