在学习了 react 的父子组件通讯后,尝试着做了一个大致如下的 todomvc 案例:
这里关于 todomvc 的基本结构及样式就不去过多的描述了
我们可以去 github todumvc 样板
上进行下载其基本的结构
大致结构如下:
index.js
/**
* 1. 导入react和react-dom
* 2. 创建 react 元素
* 3. 把 react 元素渲染到页面
*/
import React from 'react';
import ReactDom from 'react-dom/client';
import { Component } from 'react';
import './styles/base.css';
import './styles/index.css'
class App extends Component {
render() {
return (
todos
)
}
}
// 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样
const element = (
);
// 参数1:渲染的 react 元素即虚拟 DOM
// 参数2:需要渲染到哪个容器中
const root = ReactDom.createRoot(document.getElementById('root'));
root.render(element);
接着我们需要做的就是将其进行组件化拆分,大致分为如下三个部分:
在 src 目录下新建 components 文件夹,专门用来存放组件
将 src/index.js 文件进行如上图拆分
components/TodoHeader.js
import React,{ Component } from 'react';
export default class TodoHeader extends Component {
render() {
return (
todos
)
}
}
components/TodoMain.js
import React,{ Component } from 'react';
export default class TodoHeader extends Component {
render() {
return (
)
}
}
components/TodoFooter.js
import React,{ Component } from 'react';
export default class TodoHeader extends Component {
render() {
return (
)
}
}
index.js
/**
* 1. 导入react和react-dom
* 2. 创建 react 元素
* 3. 把 react 元素渲染到页面
*/
import React from 'react';
import ReactDom from 'react-dom/client';
import { Component } from 'react';
import './styles/base.css';
import './styles/index.css'
import TodoHeader from './components/TodoHeader';
import TodoMain from './components/TodoMain';
import TodoFooter from './components/TodoFooter';
class App extends Component {
render() {
return (
)
}
}
// 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样
const element = (
);
// 参数1:渲染的 react 元素即虚拟 DOM
// 参数2:需要渲染到哪个容器中
const root = ReactDom.createRoot(document.getElementById('root'));
root.render(element);
在完成了上面的步骤之后,从这里开始,正式进行案例的开发
首先,我们需要实现的就是 任务列表渲染功能
这里需要先在父组件(index.js)里准备数据,然后传递到子组件(TodoMain.js)里进行展示,就需要用到组件通讯里的父传子:
父组件提供要传递的state数据
给子组件标签添加属性,值为 state 中的数据
子组件中通过 props 接收父组件中传递的数据
注意:接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据
TodoMain.js
import React,{ Component } from 'react';
export default class TodoHeader extends Component {
render() {
const { list } = this.props;
return (
{
list.map(item => {
return (
)
})
}
)
}
}
index.js
/**
* 1. 导入react和react-dom
* 2. 创建 react 元素
* 3. 把 react 元素渲染到页面
*/
import React from 'react';
import ReactDom from 'react-dom/client';
import { Component } from 'react';
import './styles/base.css';
import './styles/index.css'
import TodoHeader from './components/TodoHeader';
import TodoMain from './components/TodoMain';
import TodoFooter from './components/TodoFooter';
class App extends Component {
state = {
list: [
{
id: 1,
name: 'JavaScript',
done: false
},
{
id: 2,
name: 'Vue',
done: false
},
{
id: 3,
name: 'react',
done: true
}
]
}
render() {
const { list } = this.state;
// console.log(list);
return (
)
}
}
// 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样
const element = (
);
// 参数1:渲染的 react 元素即虚拟 DOM
// 参数2:需要渲染到哪个容器中
const root = ReactDom.createRoot(document.getElementById('root'));
root.render(element);
其次,我们需要实现的就是 删除任务功能
当点击删除按钮时,实现删除
这就需要给每个 li 注册点击事件,实现删除
但是需要注意的就是 li 的渲染是在 TodoMain.js 里的,而数据是在 index.js(父组件)里,因此就需要用到组件通信里的子传父通过当前点击的 id 进行删除
思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数。
父组件提供一个回调函数(用于接收数据)
将该函数作为属性的值,传递给子组件
子组件通过 props 调用回调函数
将子组件的数据作为参数传递给回调函数
index.js
/**
* 1. 导入react和react-dom
* 2. 创建 react 元素
* 3. 把 react 元素渲染到页面
*/
...
class App extends Component {
state = {
...
}
render() {
...
...
}
delTodoById = (id) => {
this.setState({
list: this.state.list.filter(item => item.id !== id)
})
}
}
...
TodoMain.js
import React,{ Component } from 'react';
export default class TodoHeader extends Component {
render() {
const { list } = this.props;
return (
...
{
list.map(item => {
return (
...
...
)
})
}
)
}
// 删除
del = (id) => {
// console.log(id);
this.props.delTodoById(id);
}
}
然后,我们需要实现的就是 任务状态修改功能
这个主要就是改变 checked 的一个状态即可
但是状态的改变是定义在index.js(父组件)的state里的--done
因此只能通过组件通讯中的子传父的形式进行传递,将当前点击的那个id,传递给父组件,在父组件里找到并修改对应id的done值
index.js
/**
* 1. 导入react和react-dom
* 2. 创建 react 元素
* 3. 把 react 元素渲染到页面
*/
...
class App extends Component {
state = {
list: [
{
id: 1,
name: 'JavaScript',
done: false
},
{
id: 2,
name: 'Vue',
done: false
},
{
id: 3,
name: 'react',
done: true
}
]
}
render() {
const { list } = this.state;
return (
...
...
)
}
// 修改任务状态
updateDone = (id) => {
this.setState({
list: this.state.list.map(item => {
if(item.id === id){
return {
...item,
done: !item.done
};
}else {
return item;
}
})
})
}
}
// 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样
const element = (
);
// 参数1:渲染的 react 元素即虚拟 DOM
// 参数2:需要渲染到哪个容器中
const root = ReactDom.createRoot(document.getElementById('root'));
root.render(element);
TodoMain.js
import React,{ Component } from 'react';
export default class TodoHeader extends Component {
render() {
const { list } = this.props;
return (
...
{
list.map(item => {
return (
...
this.updateDone(item.id)} />
...
)
})
}
)
}
// 修改任务状态
updateDone = (id) => {
this.props.updateDone(id);
}
}
接着,我们需要实现的是 添加任务 功能
对于这个功能的实现主要利用的就是键盘的回车事件
在 TodoHeader.js 中通过 value 拿到值
但是数据的增加是在 index.js 里的,也就是最终需要把数据添加到 index.js(父组件)的 list 里,因此就需要用到组件通讯中的子传父
import React,{ Component } from 'react';
export default class TodoHeader extends Component {
state = {
name: ''
}
render() {
return (
todos
)
}
// 添加任务
addTas = (e) => {
this.setState({
name: e.target.value
})
}
addTo = (e) => {
// 按下回车键后才触发
if(e.keyCode !== 13){
return;
}
if(!this.state.name.trim()){
return alert('任务不能为空!');
}
// 添加 todo
this.props.addTo(this.state.name);
// 清空 name
this.setState({
name: ''
})
}
}
index.js
/**
* 1. 导入react和react-dom
* 2. 创建 react 元素
* 3. 把 react 元素渲染到页面
*/
...
class App extends Component {
state = {
list: [
{
id: 1,
name: 'JavaScript',
done: false
},
{
id: 2,
name: 'Vue',
done: false
},
{
id: 3,
name: 'react',
done: true
}
]
}
render() {
const { list } = this.state;
return (
...
...
)
}
...
// 添加任务
addTo = (name) => {
// console.log(name);
this.setState({
list: [{
id: Date.now(),
name: name,
done: false
} ,
...this.state.list]
})
}
}
// 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样
const element = (
);
// 参数1:渲染的 react 元素即虚拟 DOM
// 参数2:需要渲染到哪个容器中
const root = ReactDom.createRoot(document.getElementById('root'));
root.render(element);
紧接着,我们需要实现的就是 双击显示 修改框
为了准确找到当前双击的这个id ,在 TodoMain.js 里通过 state 保存了一个当前双击的那个任务的 id 的状态(currentId),将当前双击的这个 id 传进去
当双击 lable 标签之后,拿到其对应的 id,并且保存到 currentId 里
在 li 里去判断 item.id 是否等于 currentId 但是这里需要同时控制两个 类名,因此我们需要通过 npm install classnames 下载 classnames
TodoMain.js
import React,{ Component } from 'react';
import classnames from 'classnames';
export default class TodoHeader extends Component {
state = {
// 存放当前双击的id
currentId: ''
}
render() {
const { list } = this.props;
return (
...
{
list.map(item => {
return (
...
...
)
})
}
)
}
...
// 双击显示修改框
showEdit = (id) => {
// console.log(id);
this.setState({
currentId: id
})
}
}
实现双击回显任务名称
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)