源码:
react-rookie 项目
接上一篇的搭建项目。接下来是写一个 Login,重点说明一下本项目只是针对 react 入门,巩固react开发以及其语法。并不是很专业。大神请绕路噢。
- 上篇已经将 Login 的route 添加到 route.config.js了
- 在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() //组件内容
}
- 利用 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>
- 小插曲之 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;
}
- 表单编写
回到 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>
- 其中 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。
其他文章后续更新--