【React入门实践】结合Ant-Design从0带你手把手开发【个人中心-信息修改】页面

如果一块表走的不准,那它每一秒都是错的。

写在前面的话:博主之前一直使用的vue框架,最近入手公司项目,开始一点点接触React,我觉得学习任何东西的最好方法就是使用它、体验他。其实完成页面前我根本没有读过react的官方文档(是有些许枯燥)但我会仿照别人的代码一点点尝试,用过之后再去看官方使用文档会豁然开朗。这个方法值得推荐。因此,咱们今天来初试React开发,实现简单的个人中心-信息修改页面 


1. 看页面需求

首先看一下页面需求。分析:进入页面调用接口初始化个人信息,修改后点击“保存”按钮,调接口保存数据后刷新页面。

 2. 分析接口文档

页面初始化加载列表接口为detail,无需请求参数,携带用户token即可,返回数据包含mobile、email;    信息修改接口为updateUserInfo,包含两个请求参数,mobile、email必填

 

 3. React目录文件夹说明

每一个模块定义一个文件夹,包含page.tsx(页面HTML样式)model.ts(编写接口)style.less(CSS样式)

4. 编写页面代码

在page.tsx页面中写

function Container({ dispatch, form, container: { editData } }: any) {
  const { validateFields} = form

  return (
    <PageHeaderLayout title="列表" className={'commonList'}>
      <Card
        className={styles.table}
        style={{ marginTop: 1, marginLeft: 1 }}
        bordered={false}
        bodyStyle={{ padding: '8px 32px 32px 32px' }}
      >
        <div style={{ textAlign: 'center', width: '700', marginLeft: '20' }}>
          <Form
            //labelAlign="left"
            layout="horizontal"
            className={styles.commonForm}
            labelCol={{ span: 3 }}
            wrapperCol={{ span: 12 }}
          >
            <Form.Item label="用户名">
              {getFieldDecorator('name', {
                initialValue: (editData && editData.name) || ''
              })(<Input placeholder="请输入" disabled />)}
            </Form.Item>
            <Form.Item label="电话号码">
              {getFieldDecorator('mobile', {
                initialValue: (editData && editData.mobile) || '',
              })(<Input placeholder="请输入" />)}
            </Form.Item>
            <Form.Item label="邮箱地址">
              {getFieldDecorator('email', {
                initialValue: (editData && editData.email) || '',
              })(<Input placeholder="请输入" />)}
            </Form.Item>
            <Button className={styles.search} onClick={onSave}>
              保存
            </Button>
          </Form>
        </div>
      </Card>
    </PageHeaderLayout>
  )
}

 5. 页面初始化-数据回显

(1)在model.ts文件里写如下代码。发送请求manage/ucenter/detail,请求参数query其实为空,并将请求返回的数据data.data赋值给editData存储在setStore里面

init: async (action, { dispatch }) => {
      const { query } = getLocation()
      const data = await dispatch({
        type: 'container/get',
        params: ['manage/ucenter/detail', query]
      })
      dispatch({
        type: 'container/setStore',
        params: [{ editData: data.data }]
      })
    },

(2)在page页的Container中添加初始化调用的函数

const init = useCallback(() => {
    dispatch('container/init')
  }, [dispatch])
  useEffect(() => {
    init()
  }, [dispatch, init])

 (3)【数据回显】在page页面通过function Container({ dispatch, form, container: { editData } }: any) 将container中的editData拿到,因此可以放在表单里的initialValue。

<Form.Item label="用户名">
    {getFieldDecorator('name', {
      initialValue: (editData && editData.name) || ''
    })(<Input placeholder="请输入" disabled />)}
</Form.Item>

 至此就完成了页面初始化,可以看到数据显示了。

6. 保存功能的实现

(1)在model页面写update接口,接口为manage/ucenter/updateUserInfo,返回数据为a

update: async ({ params: [remoteValue] }, { dispatch }) => {
      let a = await dispatch({
        type: 'container/post',
        params: ['manage/ucenter/updateUserInfo', remoteValue]
      })
      return a
    }

 (2)在保存按钮上添加点击事件onClick={onSave}并在page页面写onSave方法。其中fieldValue里面保存着form表单验证的数据对象,因此可以取到对应input的值。

const onSave = useCallback(() => {
    const { validateFields } = form
    validateFields((err: any, fieldValue: any) => {
      if (err) return
      let res = {
        mobile: fieldValue.mobile,
        email: fieldValue.email
      }
      dispatch({
        type: 'container/update',
        params: [res]
      }).then((v: any) => {
        console.log(v)
        if (v.code === 200) {
          message.success('保存成功')
          init()
        } else {
          message.error(v.message)
        }
      })
    })
  }, [dispatch, form, init])

 至此可以实现保存功能。

7. 表单验证

Ant Design 表单中getFieldDecorator、getFieldValue、setFieldValue用法Ant Design 表单中getFieldDecorator、getFieldValue、setFieldValue用法 - 健人雄 - 博客园

getFieldDecorator是一个方法,这个方法接收两个参数,第一个是表单的字段对象,第二个是验证规则。这个方法本身返回一个方法,需要将需要获取值的标签包裹进去。

(1)验证手机号

<Form.Item label="电话号码">
    {getFieldDecorator('mobile', {
     initialValue: (editData && editData.mobile) || '',
     rules: [
     { required: true, message: '电话号码不能为空' },
     {
         pattern: /^((\+)?86|((\+)?86)?)0?1[3458]\d{9}$/,
         message: '请输入正确的电话号码'
     }
    ]
  })(<Input placeholder="请输入" type="number" />)}
 </Form.Item>

(2)验证邮箱

<Form.Item label="邮箱地址">
   {getFieldDecorator('email', {
    initialValue: (editData && editData.email) || '',
    rules: [
       {
         pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
         // pattern: /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
         // pattern: /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.]){1,2}[A-Za-z\d]{2,5}$/g,
         message: '邮箱格式不正确'
       },
       {
         max: 50,
         message: '邮箱不得超过50字符'
       },
    { required: true, message: '邮箱地址不能为空' }
    ]
 })(<Input placeholder="请输入" />)}
</Form.Item>

8. style样式

.commonForm{
 margin-top: 20px;
 :global{
   .ant-input{
      width:600px;
      height:40px;
      margin-left: 10px;
  }
  .ant-form.ant-form-inline{
   display: flex !important
  }
 }
}
.search{
  width: 120px;
  height: 40px;
  opacity: 1;
  background: #2b7dff;
  border-radius: 4px;
  font-size: 14px;
  font-weight: 400;
  text-align: left;
  color: #ffffff;
  line-height: 21px;
  text-align: center;
  display: block;
  margin-left:300px
}

9. 页面效果

 10. 页面源码

// model.ts
import commonModel from '../../../../models/commonModel'
import { modelExtend } from 'tyrael'
import { listMOM } from '../../../../common/dict'
import { getLocation } from '../../../../utils/tool'
const model: ModelExtend = modelExtend(commonModel, {
  state: {
    selectedRows: [],
    listData: {},
    productData: [],
    editData: {},
    detailData: {}
  },
  namespace: 'container',
  effects: {
    init: async (action, { dispatch }) => {
      const { query } = getLocation()
      const data = await dispatch({
        type: 'container/get',
        params: ['manage/ucenter/detail', query]
      })
      dispatch({
        type: 'container/setStore',
        params: [{ editData: data.data }]
      })
    },
    update: async ({ params: [remoteValue] }, { dispatch }) => {
      let a = await dispatch({
        type: 'container/post',
        params: ['manage/ucenter/updateUserInfo', remoteValue]
      })
      return a
    }
  },
  reducers: {}
})
export default model

// page.tsx
import React, { useEffect, useCallback, useMemo, useState } from 'react'
import { connect } from 'react-redux'
import PageHeaderLayout from '../../../../components/PageHeaderLayout/index'
import { getLocation } from '../../../../utils/tool'
import styles from './style.less'
import { Form, Input, Button, Card, Select, message } from 'antd'
function Container({ dispatch, form, container: { editData } }: any) {
  const { getFieldDecorator, validateFields, getFieldValue } = form
  const { query } = getLocation()
  const init = useCallback(() => {
    dispatch('container/init')
  }, [dispatch])
  useEffect(() => {
    init()
  }, [dispatch, init])

  const onSave = useCallback(() => {
    const { validateFields } = form
    validateFields((err: any, fieldValue: any) => {
      if (err) return
      let res = {
        mobile: fieldValue.mobile,
        email: fieldValue.email
      }
      dispatch({
        type: 'container/update',
        params: [res]
      }).then((v: any) => {
        console.log(v)
        if (v.code === 200) {
          message.success('保存成功')
          init()
        } else {
          message.error(v.message)
        }
      })
    })
  }, [dispatch, form, init])

  return (
    <PageHeaderLayout title="列表" className={'commonList'}>
      <Card
        className={styles.table}
        style={{ marginTop: 1, marginLeft: 1 }}
        bordered={false}
        bodyStyle={{ padding: '8px 32px 32px 32px' }}
      >
        <div style={{ textAlign: 'center', width: '700', marginLeft: '20' }}>
          <Form
            //labelAlign="left"
            layout="horizontal"
            className={styles.commonForm}
            labelCol={{ span: 3 }}
            wrapperCol={{ span: 12 }}
          >
            <Form.Item label="用户名">
              {getFieldDecorator('name', {
                initialValue: (editData && editData.name) || ''
              })(<Input placeholder="请输入" disabled />)}
            </Form.Item>
            <Form.Item label="电话号码">
              {getFieldDecorator('mobile', {
                initialValue: (editData && editData.mobile) || '',
                rules: [
                  { required: true, message: '电话号码不能为空' },
                  {
                    pattern: /^((\+)?86|((\+)?86)?)0?1[3458]\d{9}$/,
                    message: '电话号码格式不正确'
                  }
                ]
              })(<Input placeholder="请输入" type="number" />)}
            </Form.Item>
            <Form.Item label="邮箱地址">
              {getFieldDecorator('email', {
                initialValue: (editData && editData.email) || '',
                rules: [
                  {
                    pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
                    // pattern: /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
                    // pattern: /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.]){1,2}[A-Za-z\d]{2,5}$/g,
                    message: '邮箱格式不正确'
                  },
                  {
                    max: 50,
                    message: '邮箱不得超过50字符'
                  },
                  { required: true, message: '邮箱地址不能为空' }
                ]
              })(<Input placeholder="请输入" />)}
            </Form.Item>
            <Button className={styles.search} onClick={onSave}>
              保存
            </Button>
          </Form>
        </div>
      </Card>
    </PageHeaderLayout>
  )
}
export default connect(({ container, loading }: any) => {
  return {
    container,
    loading: loading.models.container
  }
})(Form.create()(Container))

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白Rachel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值