react案例动态表单(受控组件)

学了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使用的一个体现。如果有问题希望大家可以指出。

相关推荐
某公司摸鱼前端30 分钟前
uniapp socket 封装 (可拿去直接用)
前端·javascript·websocket·uni-app
要加油哦~31 分钟前
vue | 插件 | 移动文件的插件 —— move-file-cli 插件 的安装与使用
前端·javascript·vue.js
小林学习编程37 分钟前
Springboot + vue + uni-app小程序web端全套家具商场
前端·vue.js·spring boot
柳鲲鹏38 分钟前
WINDOWS最快布署WEB服务器:apache2
服务器·前端·windows
weixin-a153003083162 小时前
【playwright篇】教程(十七)[html元素知识]
java·前端·html
ai小鬼头2 小时前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
一只叫煤球的猫3 小时前
普通程序员,从开发到管理岗,为什么我越升职越痛苦?
前端·后端·全栈
vvilkim3 小时前
Electron 自动更新机制详解:实现无缝应用升级
前端·javascript·electron
vvilkim3 小时前
Electron 应用中的内容安全策略 (CSP) 全面指南
前端·javascript·electron
aha-凯心3 小时前
vben 之 axios 封装
前端·javascript·学习