首页 > 其他分享 >React练习实例-TodoList

React练习实例-TodoList

时间:2023-01-11 10:56:12浏览次数:49  
标签:删除 Header TodoList 待办 React Item state 实例 组件

目标

  1. 顶部输入框中输入任务(字符串),敲击回车键后,中间新出现一个代办项
  2. 鼠标放在单个代办项,右侧出现删除按钮,点击删除代办项
  3. 选中多个代办项,点击右下角“清除已完成”按钮,删除所有被选中的待办项

组件设计

除整体App组件外,初步设计为4个组件:

  1. Header:顶部输入框
  2. List:中间所有的代办列表
  3. Item:待办列表中的一项
  4. Footer:底部显示统计状态信息和“删除所有”按钮所在栏

详细设计

肯定涉及到官方入门程序中“状态提升”的概念:兄弟组件将自己的状态交给父组件管理(自己就变成了一个“受控组件”)

  • Header首先要有一个成员变量接收输入的字符串
  • 其次要新建一个Item待办项,并将输入串交给它
  • 我们还希望同时页面能自动重新渲染,这就涉及到了某个state。具体哪一个呢?
  • Footer要同步更新总共的待办项数量

这么看来,应该在App中准备两个变量,嗯…突然想到了这一系列动作其实都是“回车键”触发的,所以其实暂时不用state
但是…手动重新渲染页面吗?…还是用state吧
仔细想,这个
敲下回车->Header获取输入框的内容,交给List(事实上应该是修改了App的一个属性),并清空输入框->List根据获取到的字符串,新建一个Item组件->修改Footer显示的Item数量->重新渲染页面
我该用怎样的数据结构来保存这些Item?需要能够被选中删除,而且不定长
答案是:一个对象数组,但是怎么获取任意对象?

实现

输入并按下回车键,新增一项到待办列表

  1. 首先(由Header)监听键盘事件,当回车键回弹后触发
<!--为输入框绑定键盘事件-->
<input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={this.handleKeyUp}/>
// 事件处理函数中判断是否是“回车键”以及输入是否为空
handleKeyUp = (e) => {
    if (e.key !== 'Enter' || e.target.value.trim() === '') return;
}
  1. 因为Header获取的输入需要传递给List,很明显兄弟组件之间的传值,状态提升将变量保存在父组件App中,至少是个数组
    但是这里的每一项又是有状态的(比如默认是否选中(完成)),于是这里被定义为一个对象数组
    // 为什么这里要放在state中?因为每次回车触发向数组中添加一个对象后,我都希望立即出现在下面的列表中,即页面的重新渲染,state可以方便地做到这一点
    constructor(props) {
        super(props);
        this.state = {
            items: [
                {id: '1', label: '吃饭', done: true},
                {id: '2', label: '睡觉', done: true},
                {id: '3', label: '敲代码', done: false}
            ]
        }
    }
  1. 保存在App状态中的数据改变了还不够,还需要List获取并遍历,构造Item组件
    父传子就很简单通过props
// 思考:页面重新渲染,传参也会重新传一遍吗?
<List items={items}/>

然后就是List遍历这个数组并构造Item组件,注意这里指定key

其实这里不是很明白怎么回事

        return (
            <ul className="todo-main">
                {
                    items.map((item) => {
                        /*return <Item key={item.id} id={item.id} name={item.name} done={item.done}/>*/
                        return <Item key={item.id} {...item}/>
                    })
                }
            </ul>
        )

Item组件需要把对象的信息展示出来

        return (
            <li className="item">
                <label>
                    <input type="checkbox" defaultChecked={done}/>
                    <span>{label}</span>
                </label>
            </li>
        )
  1. 子组件Header修改父组件state,是通过父组件向子组件传递一个可以修改自己state地函数来做到的
    // 这个函数接收一个待办项对象为参数,把他添加到列表数组并更新state触发刷新
    // 思考:为什么这里构造了一个新数组而不是直接修改?
    addItem = (item) => {
        const {items} = this.state;
        const newItems = [item, ...items];
        this.setState({items: newItems});
    }
// 把这个函数交给子组件
<Header addItem={this.addItem}/>
  1. Header中直接获取的仅仅是一个字符串,我们需要额外的参数构造一个对象并交给父组件提供的方法

nanoid是一个库,提供一个UUID

        const item = {id: nanoid(), label: e.target.value, done: false};
        this.props.addItem(item);
        e.target.value = "";

至此,功能点1完成(当然,Footer中的实时统计信息还没做)

鼠标放在单个代办项,右侧出现删除按钮,点击删除代办项

首先是鼠标移至待办项上,待办项的交互响应:

  1. 背景色改变:这个可以通过 伪类选择器:hover轻易实现
  2. “删除”按钮出现,这个只能绑定监听事件,监听鼠标移入和移出
            <li className="item"
                onm ouseEnter={this.handleMouse(true)} onm ouseLeave={this.handleMouse(false)}>
            </li>

处理事件中仅仅是修改了保存在state中的标志位mouseEnter,当然,这也是为了能够自动渲染

    handleMouse = (flag) => {
        return () => {
            this.setState({mouseEnter: flag});
        }
    }

而这个标志位又决定了“删除按钮”是否显示

<button className="btn btn-danger" style={{display: mouseEnter ? 'block' : 'none'}}>删除</button>

至此,功能2完成
来看一眼目前的样子

标签:删除,Header,TodoList,待办,React,Item,state,实例,组件
From: https://www.cnblogs.com/yaocy/p/17040892.html

相关文章