react函数式组件写todolist(原生)
最终结果:
第一步. 布置结构, 如下这样
每一个文件夹里面都有一个index.jsx与index.css
第二步. 实现列表显示页面
1.先在App.jsx里面初始化数据
2.把App.jsx里面的todo通过List.jsx组件传给item.jsx以显示列表数据
第三步. 实现新增功能
1.在App.jsx里面写一个添加一个对象的方法addTodo,将addTodo方法给Header.jsx组件,Header.jsx组件通过这个方法拿到input里面的值作为一个对象传为App.jsx 2.子组件中需要判断是否是回车按钮以及值是否为空,为空显示出一个弹窗不能为空
第四步.勾选与取消功能
App.jsx中定义一个勾选与取消的方法chekedTodo,通过List.jsx传给item.jsx,拿到input的id与当前的值给父组件,去判断拿到的id与原本的数据中的每一个id对比是否一致,一致的话,就改变当前的done的状态并且把状态值与以前的todo合并起来,否则就原封不动,最后把更新后的值重新设置一下
第五步.删除一个todo
App.jsx里面设置删除方法,通过这个方法传给item.jsx去绑定到button按钮上,拿到当前要删除的id,通过filter得到id不相等的对象显示到列表上
第六步. 清除已经勾选的功能
App.jsx里面设置特定的方法,将todoObj.done为true的删除
第七步. 全选
App.jsx设置方法拿到子组件里面的done,把当前的值true或false给原数据的每一个todo
//App.jsx
import Header from './Header'
import Footer from './Footer'
import List from './List'
import { useState } from 'react';
export default function App() {
// 初始化数据
const [todos, setTodos] = useState([
{ id: '01', name: '吃饭', done: true },
{ id: '02', name: '睡觉', done: true },
{ id: '03', name: '打代码', done: false },
{ id: '04', name: '打代码111', done: true }
])
// 添加一个todo对象,并且更新状态
const addTodo = (todoObj) => {
setTodos([...todos, todoObj])
}
// 勾选与取消,并且更新状态
const checkedTodo = (id, done) => {
// 判断子组件传过来的值与父组件原本的值的id, 相等的话就是找到了选中的,返回原本的值并且覆盖done
const newTodos = todos.map(todo => {
if (todo.id == id) {
return { ...todo, done }
}
// 不等就原封不动的返回原值
else {
return todo
}
})
setTodos(newTodos)
}
// 删除一个todo
const deleteTodo = (id) => {
const newTodos = todos.filter(todo => todo.id !== id)
setTodos(newTodos)
}
// 全选
const checkAllTodo = (done) => {
const newTodos = todos.map(todo => {
// 注意点:3
return { ...todo, done }
})
setTodos(newTodos)
}
// 清除已经勾选的
const clearTodos = () => {
const newTodos = todos.filter(todoObj => {
return !todoObj.done
})
setTodos(newTodos)
}
return (
<div className="todo-wrap">
<Header addTodo={addTodo} />
<List todos={todos} checkedTodo={checkedTodo} deleteTodo={deleteTodo} />
<Footer todos={todos} checkAllTodo={checkAllTodo} clearTodos={clearTodos} />
</div>
)
}
// Header.jsx
export default function Header({addTodo}){
const handleAdd = (event) => {
// 判断并且拿到值传给父组件
const {keyCode, target} = event
if(keyCode !== 13) return
// 判断不能为空
if(target.value == ''){
alert('不能为空')
return
}
const newTodoObj = {id: nanoid(), name: target.value, done:false}
addTodo(newTodoObj)
// 清空input框
target.value = ''
}
return (
<div className="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={handleAdd}/>
</div>
)
}
// Footer.jsx
export default function Footer({todos, checkAllTodo, clearTodos}){
// 已完成变量
const doneCount = todos.reduce((pre, current)=> pre+(current.done?1:0), 0)
const handleCheckedAll = (event) =>{
checkAllTodo(event.target.checked)
}
return (
<div className="todo-footer">
<label>
<input type="checkbox" onChange={handleCheckedAll} checked={doneCount == todos.length && todos.length !== 0? true : false}/>
</label>
<span>
<span>已完成{doneCount}</span> / 全部{todos.length}
</span>
{/* 1 */}
<button className="btn btn-danger" onClick={()=>clearTodos()}>清除已完成任务</button>
</div>
)
}
// List.jsx
export default function List({todos, checkedTodo, deleteTodo}){
return (
<ul className="todo-main">
{
todos.map(todo=>{
return <Item {...todo} key={todo.id} checkedTodo={checkedTodo} deleteTodo={deleteTodo}/>
})
}
</ul>
)
}
// item.jsx
export default function Item({ id, name, done, deleteTodo, checkedTodo }) {
// 鼠标的移入与移出
const [mouse, setMouse] = useState(false)
const handleMouse = (flag) => {
setMouse(flag)
}
return (
<li onMouseEnter={() => handleMouse(true)} onMouseLeave={() => handleMouse(false)}>
<label>
<input type="checkbox" onChange={(e) => checkedTodo(id, e.target.checked)} checked={done} />
<span>{name}</span>
</label>
<button className="btn btn-danger" onClick={() => deleteTodo(id)} style={{ display: mouse ? 'block' : 'none' }}>删除</button>
</li>
)
}
转载自:https://juejin.cn/post/7352837711340912691