react 入门实践(2)-登录页

源码:
react-rookie 项目

接上一篇的搭建项目。接下来是写一个 Login,重点说明一下本项目只是针对 react 入门,巩固react开发以及其语法。并不是很专业。大神请绕路噢。

  1. 上篇已经将 Login 的route 添加到 route.config.js了
  2. 在pages文件夹新建 login 文件夹 ,然后里边新建login.css以及 index.jsx
//登陆页面用到 Tabs 
//还有 antd UI框架自带的一些布局容器
import styles from './login.css'
import { Layout, Button, Form, Input, Tabs, message } from 'antd';
import React from 'react';

const { Header, Content, Footer } = Layout;
const { TabPane } = Tabs;

//布局组件的CSS 也要配置
const layoutcss = {
    minHeight: '100%',
    width: '100%'
}
const layout = {
    labelCol: {
        span: 6,
    },
    wrapperCol: {
        span: 14,
    },
}
const tailLayout = {
        wrapperCol: {
            offset: 8,
            span: 16,
        },
    };

3.在登录页面写两种登录方式 一个是手机号一个是用户名,所以分两个 form表单

export default function Login() {

  const [form] = Form.useForm();
  const [form2] = Form.useForm();
  const onFinish = (values) => {}; //表单提交函数1
  const onFinish2 = (values) => {};//表单提交函数2
const onFill = () => {
  form.setFieldsValue({
            name: 'luzhihao',
            pwd: 'asdasd',
        });
} //表单填充函数
  const PhoneValidation = async (v) => {
        try {
            const values = await form2.validateFields(['phone']);
            message.success({
                content: '短信验证码已发送到您的手机!',
                style: {
                    marginTop: '20vh',
                },
            });
            return true;
        } catch (errorInfo) {
            console.log('Failed:', errorInfo);
            return false;
        }
    }

  return() //组件内容
}
  1. 利用 layout 和 content 作为页面的基本布局
//在 return ()里添加以下代码, 其中 content 是主要内容的容器

<Layout style={layoutcss}>
  <Layout>
    <Header className="site-layout-sub-header-background"
       style={{ padding: 0 }} />
    <Content className={styles.bg}>
    //先空着
    </Content>
  <Footer  style={{  textAlign: 'center' }} >
    Ant Design ©2018 Created by Ant UED
  </Footer>
    </Layout>
</Layout>
  1. 小插曲之 module.css
    看到 content 的 className 为什么是 styles.bg?
    以前开发 vue 的时候 css 可以用 scope 来声明域 的作用范围,但是react并不能这么做。因此由了 css module 。
    注::使用Create React App 第二版本构建的React项目,在使用CSS Modules时,不需要做任何的配置。只需要创建.css.sass.sass文件时有相应的要求,即**使用 [name].module.css 文件命名约定支持 CSS Modules 和常规 CSS 。 CSS Modules 允许通过自动创建 [filename]\_[classname]\_\_[hash] 格式的唯一 classname 来确定 CSS 的作用域。同样的,如是要是.sass.scss的话,文件名格式应该是[name].module.sass[name].module.scss著作权归作者所有。
    原文: https://www.w3cplus.com/react/css-modules-in-react.html © w3cplus.com
将 login.css 改为 login.module.css 并且加入以下css
css 导入改为这样:
import styles from './login.module.css'

.bg {
    background: linear-gradient(180deg, #a0d7e7, #6c5dd3);
    text-align: center;
    justify-content: center;
    display: flex;
    align-items: center;
  
  }
  
  .login_card {
    width: 520px;
    background-color: #303030;
    border-radius: 20px;
    margin: auto;
    text-align: center;
    justify-content: center;
    padding: 51px 60px;
    min-height: 100%;
  }
  
  .login-button {
    width: 400px;
    height: 56px;
    background: #6c5dd3;
    border-radius: 12px;
  }
  
  .heard {
    position: absolute;
    display: flex;
    top: 264px;
  }
  .title {
    width: 315px;
    font-size: 30px;
    font-family: Arial;
    font-weight: bold;
    color: #151830;
  }
  .cloud {
    width: 100px;
    height: 72px;
    line-height: 72px;
  }
  .cloud img {
    width: 40px;
    height: 40px;
  }
  .footer {
    width: 100%;
    height: 12px;
    font-size: 10px;
    font-family: Microsoft YaHei;
    font-weight: 300;
    color: #151830;
    background: none;
    bottom: 34px;
    left: 0;
    position: absolute;
  }
  1. 表单编写
    回到 Content 元素内 我们将利用 tabs 来实现两个表单之间的切换
    tab 属性自定义标签名称 rules 是规则
import FormButton from '@/components/formButton';
import CodeInput from '@/components/codeInput';
//这两个组件 在第7步开始创建

<div className={styles.login_card}>
  <Tabs type='card' style={{ minHeight: '260px' }}>
    <TabPane tab='用户名登录' key='1'>
      <Form {...layout} form={form} name="control-hooks" onFinish={onFinish}>
        <Form.Item    name="name"   label="Name"
            rules={[
                {
                     required: true,
                      message: '请输入用户名/手机号'
                    }
               ]}  >
               <Input placeholder='请输入用户名/手机号' />
          </Form.Item>
          <Form.Item   name="pwd"  label="password"
                   rules={[
                       {
                           required: true,
                          message: '请输入密码'
                       }
                    ]}
                >
                   <Input.Password placeholder='密码' />
           </Form.Item>
          <Form.Item {...tailLayout}>
              <FormButton form={form}></FormButton>
                   <Button type="link" htmlType="button" onClick={onFill}>
                             Fill form
                  </Button>
          </Form.Item>
       </Form>
    </TabPane>

   <TabPane tab='手机号登录' key='2'>
      <Form {...layout} form={form2} name="codeForm" onFinish={onFinish2}>
          <Form.Item   name='phone'   label='Phone'
                validateTrigger='onBlur'
                      rules={[
                            {
                               required: true,
                              label: '手机号',
                               message: '请输入手机号码'
                            },
             { pattern: /^1[3|4|5|7|8][0-9]\d{8}$/, message: '手机号格式错误!' }]}>
                 <Input placeholder='请输入手机号码'></Input>
           </Form.Item>
             <CodeInput PhoneValidation={PhoneValidation} />
              <Form.Item {...tailLayout}>
                  <FormButton form={form2}></FormButton>
              </Form.Item>
          </Form>
        </TabPane>
   </Tabs>
 </div>
  1. 其中 FormButton 和 CodeInput 是封装的组件
    CodeInput 主要是手机号验证码输入以及倒计时 功能 封装
    根据 6. 的代码 在 components 文件夹下新建两个同名文件夹和 index.jsx
FormButton 文件夹下的 index.jsx

import { Button } from 'antd';
import React from 'react';
export default function FormButton(props) {
    const onReset = () => {
        props.form.resetFields();
    };

    return (
        <>
            <Button type="primary" htmlType="submit">
                Submit
            </Button>
            <Button htmlType="button" onClick={onReset} style={{
                marginLeft: '30px'
            }}>
                Reset
            </Button>
        </>
    )
}

此处 使用 useState 来倒计时验证码发送的秒数。
useEffect 来判断 time 变量的 临界值 60 与 0
notifyPhoneValidation 用来通知组件去验证电话号码格式的验证
liked 用来转换 button 的文字内容

//CodeInput 的 index.jsx 代码
import React from "react";
import { Form, Input } from "antd";
import { useState, useEffect } from 'react';

let timer = null;
const CodeButton = (props) => {
    const liked = props.liked
    const toSetLiked = props.toSetLiked
    const [time, setTime] = useState(0)
    const timeTip = `(${time})秒后重发`
    useEffect(() => {
        timer && clearInterval(timer);
        return () => timer && clearInterval(timer);
    }, []);

    useEffect(() => {
        if (time === 60) timer = setInterval(() => setTime(time => --time), 1000)
        else if (time === 0) { clearInterval(timer); toSetLiked(true) }
    }, [time, toSetLiked])

    async function sendCode() {
        let result = await props.notifyPhoneValidation()
        if (result) {
            //TODO:去服务端发送需要验证码的请求
            toSetLiked(false)
            setTime(60)
        }
    }


    return (
        <button
            //判断如果点击了获取验证码,就让button按钮上显示 *秒后重发送 并且button设置为disabled
            disabled={liked ? false : true}
            style={{ backgroundColor:'black',fontSize: '12px', border: 'none', caretColor: 'transparent' }}
            type="button"

            onClick={sendCode} //点击此按钮获取验证码
        >
            {liked ? "Get验证码" : timeTip}
        </button>
    )

}
export default function CodeInput(props) {
    const [liked, setLiked] = useState(true)
    async function notifyPhoneValidation() {
        return props.PhoneValidation()
    }
    return (
        <Form.Item
            name="zcode"
            label="Code"
            validateTrigger="onBlur"
            rules={[
                {
                    required: true,
                    label: "Code",
                    message: "请输入验证码",
                },
                { pattern: /^[0-9]{4}$/, message: "验证码格式错误!" },
            ]}
        >
            <Input   placeholder="请输入验证码"
                addonAfter={
                    <CodeButton toSetLiked={(v) => { setLiked(v) }} liked={liked} notifyPhoneValidation={notifyPhoneValidation} />
                }
            ></Input>
        </Form.Item>
    );
}

自此login页面基本完成,页面涉及到的 useStatte useEffect 比较多,这种写法是 react 新版的 hooks 写法,官方也推荐以后用这种写法。配合函数式组件来开发,我个人也觉得比 class 组件的写法简洁高效。

刚入门的小伙伴可以去 官方看看 这几个hooks。

其他文章后续更新--
\color{#228B22}{能力有限,欢迎指教}
\color{red}{警: 禁止抄袭,转载说明出处 }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,639评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,093评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,079评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,329评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,343评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,047评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,645评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,565评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,201评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,338评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,014评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,701评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,194评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,685评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,345评论 2 358

推荐阅读更多精彩内容