react案例动态表单(受控组件)
学了react基础的这些state,props,以及配置react.react-dom,react-scriprs这些环境,和组件,检验这些最直接的方式就是写出点什么,现在我们去写一个受控组件(用state去操作渲染标签内容,用标签的value去操作更新state,双向绑定),那么我们开始吧。
首先要写一个表单,先写出来结构。
首先是用户输入区域,还有展示区,首先展示区有默认展示的内容,我们直接定义一个state去代替服务器返回的数据。然后这是一个数组对象,我们的输入区就是输入之后,动态的改变我们state数组对象,然后展示区只管重新遍历数组对象然后展示。还有删除,以及加了一个筛选器,其实根本的实现原理就是改变我们的state数组对象,因为最本质的页面内容就是靠展示这些数据的,然后无非就是样式以及html结构。我们开始吧。
1.实现展示区
首先就是下面的展示区,我们分组件,一个壳子组件里面展示的是列表,都是重复的所以遍历一个组件即可。ok展示区是两个组件,然后列表我们左右两个区域,我们在分一下组件,也就是左边日历是列表的子组件,结构清晰了开始写代码吧。
//函数组件 组件的首字母必须大写 函数组件就是一个返回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。
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,然后是列表组件
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.实现表单动态输入改变展示区
那么下一个就是用户输入内容,展示区新添加展示列表了。首先就是输入框作为一个组件。
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.valueconst 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的元素。然后删除就实现了。
当然我们删除时候是不可逆的,那么就需要一个提示框,在每次删除前强制用户看到这个,让她们判断是否确定删除。
我们去创造一个组件,
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去控制只有删除事件发生的时候才显示。但是这里有一个问题,显示的框需要阻止其他行为,也就是必须盖住页面,用户只能去选择。我们就需要一个壳子在页面上面,壳子上面是这个提示框。
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去定义一个容器展示这个外壳,保证是在图层最上面。然后外壳渲染到这个位置。然后再
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去判断是否留下。
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使用的一个体现。如果有问题希望大家可以指出。