学了react基础的这些state,props,以及配置react.react-dom,react-scriprs这些环境,和组件,检验这些最直接的方式就是写出点什么,现在我们去写一个受控组件(用state去操作渲染标签内容,用标签的value去操作更新state,双向绑定),那么我们开始吧。
首先要写一个表单,先写出来结构。
编辑
首先是用户输入区域,还有展示区,首先展示区有默认展示的内容,我们直接定义一个state去代替服务器返回的数据。然后这是一个数组对象,我们的输入区就是输入之后,动态的改变我们state数组对象,然后展示区只管重新遍历数组对象然后展示。还有删除,以及加了一个筛选器,其实根本的实现原理就是改变我们的state数组对象,因为最本质的页面内容就是靠展示这些数据的,然后无非就是样式以及html结构。我们开始吧。
1.实现展示区
首先就是下面的展示区,我们分组件,一个壳子组件里面展示的是列表,都是重复的所以遍历一个组件即可。ok展示区是两个组件,然后列表我们左右两个区域,我们在分一下组件,也就是左边日历是列表的子组件,结构清晰了开始写代码吧。
javascript
//函数组件 组件的首字母必须大写 函数组件就是一个返回jsx的普通函数
//类组件也是必须返回一个jsx不过需要继承Component然后还需要调用render方法去返回jsx
import React from 'react'
import Logs from './components/Log'
import './App.css'
import LogsForm from './components/LogsForm'
export default function App() {
const [logsData, setLogsData] = React.useState([{
id: "001",
date: new Date(2021, 9, 20, 19, 0),
title: '学习九阳神功',
time: 30
},
{
id: "002",
date: new Date(2021, 4, 20, 19, 0),
title: '学习我我我功',
time: 20
},
{
id: "003",
date: new Date(2021, 5, 20, 19, 0),
title: '学习你你神功',
time: 10
},
{
id: "004",
date: new Date(2022, 1, 20, 19, 0),
title: '学习九大大滴神功',
time: 34
}
])
const onHandle = (newLog) => {
newLog.id = Date.now() + ''
setLogsData(() => {
return ([newLog, ...logsData])
})
}
const deleteHandle = (id) => {
// setLogsData((preState) => {
// const newLogs = [...preState]
// newLogs.splice(index, 1)
// return newLogs
// })
console.log('zhesi id', id);
setLogsData((preState) => {
return (preState.filter((item) => { return (item.id !== id) }))
})
}
return (
<div className='app'>
<LogsForm onHandle={onHandle} />
<Logs logsData={logsData} deleteHandle={deleteHandle} />
</div>
)
}
这是App外壳组件,也就是所有组件都在这里超级拼装成页面。不多说了,因为是所有组件的根组件也就是父亲,我们把数据默认放在这里。当作是服务器响应回来的数据。然后传给展示组件Logs。
javascript
import React from 'react'
import LogItem from '../LogItem'
import './index.css'
import Logfilter from '../Logfilter'
import Card from '../UI/Card'
export default function Logs(props) {
//模拟一组从服务器加载的数据
const [year, setYear] = React.useState(2022)
let filterDate = props.logsData.filter((item) => { return (item.date.getFullYear() === year) })
let contetn = filterDate.map((item) => {
return (
<LogItem key={item.id} deleteHandle={() => { props.deleteHandle(item.id) }} date={item.date} title={item.title} time={item.time} />
// <LogItem {...item} />
)
})
if (contetn.length === 0) {
contetn = <p className='lll'>现在列表空了</p>
}
const handleYear = (year) => {
setYear(() => {
return year
})
}
return (
// 如果组件中的数据全部写死会导致组件无法动态设置
//希望组件数据可以由外部设置 在组件间父组件可以通过props给子组件传递数据
<Card className='logs'>
<Logfilter year={year} handleYear={handleYear} />
{/* 在父组件给子组件设置属性在函数组件中可以通过参数来保存 */}
{
contetn
}
</Card>
)
}
okLogs组件通过props接收,然后我们遍历数组,然后每次渲染都给子组件传递当前元素的展示内容,通过props,然后是列表组件
javascript
import React from 'react'
import MyDate from '../MyDate'
import Card from '../UI/Card'
import ConFirmModer from '../UI/ConFirmModel'
import './index.css'
export default function LogItem(props) {
// 添加一个state 记录是否显示窗口
const [show, setShow] = React.useState(false)
// console.log(props);
// console.log(props.data);
const handleDelete = () => {
//confirm执行前询问
// const del = window.confirm('确认吗该操作不可恢复');
setShow(() => {
return (true)
})
// if (del) {
// //删除当前的item 其实就是从数据state移出指定的数据
// props.deleteHandle()
// }
}
const handleCancel = () => {
setShow(false)
}
const handleOk = () => {
props.deleteHandle()
}
return (
//函数组件的行参定义一个props props指向一个对象 包含父组件传递所有参数
<Card className="item">
{show && <ConFirmModer confirmText="该操作不可恢复,请确认" handleCancel={handleCancel} handleOk={handleOk} />}
<MyDate date={props.date} />
<div className="content">
<h2 className='title'>{props.title}</h2>
<div className="time">{props.time}</div>
</div>
{/* 创建一个删除按钮 */}
<div>
<div className='delete' onClick={handleDelete}>X</div>
</div>
</Card>
)
}
列表组件接收参数渲染,然后日历同样是这样。没什么好说的。那么展示区就完成了。
2.实现表单动态输入改变展示区
那么下一个就是用户输入内容,展示区新添加展示列表了。首先就是输入框作为一个组件。
javascript
import React from 'react'
import Card from '../UI/Card'
import './index.css'
export default function LogsForm(props) {
//创建三个变量存储表单数据
const [inputDate, setInputDate] = React.useState('')
const [title, setTitle] = React.useState('')
const [time, setTime] = React.useState('')
//将表单数据统一到一个state中
// const [formData, setFormData] = React.useState({
// inputDate: '',
// title: '',
// time: ''
// })
//获取用户输入的内容 当表单项发生变化的时候 监听事件
//创建响应函数监听表单的变化
// const titleRef = React.useRef()
//首先拿到DOM对象 用ref 或者document.getElementById()
//ref.current id.value
const dateChangeHandle = (event) => {
//事件对象event保存发生当前事件触发所以信息
//event.target执行的是触发事件的对象
// setFormData(() => {
// return ({ ...formData, inputDate: event.target.value })
// })
setInputDate(() => {
return (event.target.value)
})
}
const timeChangeHandle = (event) => {
// setFormData(() => {
// return ({ ...formData, time: event.target.value })
// })
setTime(() => {
return (event.target.value)
})
}
const titleChangeHandle = (event) => {
// setFormData(() => {
// return ({ ...formData, title: event.target.value })
// })
setTitle(() => {
return (event.target.value)
})
}
//表单提交时汇总表单中的数据 react表单不需要自行提交 通过react提交
const formSubmitHandler = (e) => {
//取消表单的默认行为
e.preventDefault()
//获取表单数据
// console.log(inputDate, title, time);
//将数据拼装成对象
const newLog = { date: new Date(inputDate), title: title, time: +time }
// console.log(newLog);
props.onHandle(newLog)
setInputDate('')
setTime('')
setTitle('')
// setFormData({
// inputDate: '',
// title: '',
// time: ''
// })
}
return (
<Card className='form'>
<form onSubmit={formSubmitHandler}>
<div className='form-item'>
<label htmlFor="date">日期</label>
<input onChange={dateChangeHandle} value={inputDate} type="date" name="" id="date" />
</div>
<div className='form-item'>
<label htmlFor="title">内容</label>
<input onChange={titleChangeHandle} value={title} type="text" name="" id="title" />
</div>
<div className='form-item'>
<label htmlFor="time">时间</label>
<input onChange={timeChangeHandle} value={time} type="text" name="" id="time" />
</div>
<div className='form-btn'>
<button>添加</button>
</div>
</form>
</Card>
)
}
三个输入框,我们需要把输入框输入的内容获取到,然后我们state数据是数组对象,我们就需要把输入的内容拼一个对象然后推进state数据里面。首先设置state然后用更新方法添加监听事件获取到输入的value值,然后在把输入框value值绑定我们设置的state。把输入组件变成受控组件,即state操作value,value操作state。。。然后表单添加事件,我们子给父传需要用调用函数时传递参数的形式,id我们用new Date()时间戳代替,这样我们就实现了这个功能。
3.删除一个列表
我们希望添加列表也可以删除列表,我们无非就是筛选数组,我们给每个列表添加一个按钮,点击的时候就传递当前这个列表的id给父组件,然后父组件通过数组的filter筛选留下所有id不是传过来的id的元素。然后删除就实现了。
当然我们删除时候是不可逆的,那么就需要一个提示框,在每次删除前强制用户看到这个,让她们判断是否确定删除。
我们去创造一个组件,
css
import React from 'react'
import BackDrop from '../BackDrop'
import './index.css'
export default function ConFirmModer(props) {
return (
<BackDrop>
<div className='confirm'>
<div className='confirm-text'>{props.confirmText}</div>
<div className='confirm-btn'>
<button onClick={props.handleOk} className='ok'>确认</button>
<button onClick={props.handleCancel}>取消</button>
</div>
</div>
</BackDrop>
)
}
.confirm {
display: flex;
flex-direction: column;
width: 400px;
height: 200px;
background-color: white;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
padding: 10px;
}
.confirm-text {
height: 150px;
display: flex;
justify-content: center;
align-items: center;
font-size: 22px;
}
.confirm-btn {
display: flex;
flex: auto;
justify-content: flex-end;
}
.confirm-btn button {
width: 100px;
margin: 0 10px;
border: none;
background-color: antiquewhite;
font-size: 20px;
}
.confirm-btn .ok {
background-color: red;
color: white;
}
当然还需要在列表里面添加一个state去控制只有删除事件发生的时候才显示。但是这里有一个问题,显示的框需要阻止其他行为,也就是必须盖住页面,用户只能去选择。我们就需要一个壳子在页面上面,壳子上面是这个提示框。
编辑
javascript
import React from 'react'
import './index.css'
import ReactDOM from 'react-dom'
//获取backdrop的根元素
const backdropRoot = document.getElementById('backdrop-root')
export default function BackDrop(props) {
return (
ReactDOM.createPortal(<div className='backdrop'>
{props.children}
</div>, backdropRoot)
)
}
这个可以需要用一个根节点去展示,在index.html去定义一个容器展示这个外壳,保证是在图层最上面。然后外壳渲染到这个位置。然后再
javascript
import React from 'react'
import BackDrop from '../BackDrop'
import './index.css'
export default function ConFirmModer(props) {
return (
<BackDrop>
<div className='confirm'>
<div className='confirm-text'>{props.confirmText}</div>
<div className='confirm-btn'>
<button onClick={props.handleOk} className='ok'>确认</button>
<button onClick={props.handleCancel}>取消</button>
</div>
</div>
</BackDrop>
)
}
然后把输入框放里面,这样就完成了删除。
4.筛选列表
我们在列表上面添加一个选择框,去筛选列表,跟删除列表一样,只不过这次是通过一个新的state去判断是否留下。
javascript
import React from 'react'
export default function Logfilter(props) {
const handleY = (event) => {
console.log(event.target.value);
props.handleYear(+event.target.value)
}
return (
<div>
年份:<select onChange={handleY} value={props.year}>
<option value="2022">2022</option>
<option value="2021">2021</option>
<option value="2020">2020</option>
</select>
</div >
)
}
ok受控组件,只不过state是在父组件里面定义的,我们props在中间人,然后根据year筛选即可。
这是一个简单的案例,用到了props,state,一些操作数组的方法,也算是掌握基础react使用的一个体现。如果有问题希望大家可以指出。