案例
- 显示一个多选列表,每一个多选项后面有一个删除按钮,点击可删除
- 多选列表鼠标移入高亮显示并展示删除按钮,移除正常显示不展示删除按钮
- 列表上面有一个输入框,用户可以输入选项,回车添加进列表
- 列表底部有统计数据展示,展示【已完成X/全部Y】
- 底部右侧有一个清空勾选按钮,点击清除所有勾选
示意图:
项目思维
项目
node_modules——依赖库
public——公共文件目录(其中最重要的是首页的html)
src——代码库
componnents——组件库
Componnent_A——组件A
index.jsx——组件代码
index.css——组件样式代码
...
App.jsx——所有组件的父组件
App.css——App组件样式
index.js——渲染
这个目录层级,是工作中常用的层级关系,一般公司都采用这种,我们测试人员虽然不是做前端工作的,但还是需要和公司接轨
创建项目
webstorm-->File-->New-->Project...-->React,自定义一个项目名(本人创建的项目名:todo_demo),点击create
小坑:项目主目录下新建一个.env文件,添加【SKIP_PREFLIGHT_CHECK=true】
然后试运行,在控制台输入npm start,如下图所示
index
这个只是把App渲染到页面,这个是固定代码,基本不需要修改,所以先把这个写好
import React from "react";
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App/>, document.getElementById("root"));
拆分组件
App
Header:头部输入框组件
List:列表组件
Item:列表元素组件
Footer:底部数据展示&按钮组件
first:项目初始化
将每一个组件中的index中写好类式组件,快捷方式rcc,将类名更改为组件名
second:静态页面
这里需要使用到官网描述的一个知识点——状态提升,其实说到底,就是state只在父组件中维护,子组件只关心props
因为要写出静态页面,初始的数据需要先定义好
app
import './App.css';
import React, {Component} from 'react';
import Header from "./componnents/Header";
import List from "./componnents/List";
import Footer from "./componnents/Footer";
class App extends Component {
state = {
todos: [
{id: "001", name: "练习", done: false},
{id: "002", name: "react", done: true},
{id: "003", name: "vue", done: false}
]
}
render() {
const {todos} = this.state;
return (
<div>
<Header/>
<List todos={todos}/>
<Footer/>
</div>
);
}
}
export default App;
Header
import React, {Component} from 'react';
import './index.css'
class Header extends Component {
render() {
return (
<input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
);
}
}
export default Header;
Footer
import React, {Component} from 'react';
import './index.css'
class Footer extends Component {
render() {
return (
<div>
<input type="checkbox"/>
<span>已完成{}</span>/全部{}
<button>清除已完成任务</button>
</div>
);
}
}
export default Footer;
List
import React, {Component} from 'react';
import Item from "../Item";
import './index.css'
class List extends Component {
render() {
const {todos} = this.props;
return (
<ul>
{todos.map((todo) => <Item name={todo.name} key={todo.id}/>)}
</ul>
);
}
}
export default List;
Item
import React, {Component} from 'react';
import './index.css'
class Item extends Component {
render() {
const {name} = this.props;
return (
<li>
<input type="checkbox"/>
<span>{name}</span>
<button>删除</button>
</li>
);
}
}
export default Item;
third:实现逻辑
app
import './App.css';
import React, {Component} from 'react';
import Header from "./componnents/Header";
import List from "./componnents/List";
import Footer from "./componnents/Footer";
class App extends Component {
state = {
todos: [
{id: "001", name: "练习", done: false},
{id: "002", name: "react", done: true},
{id: "003", name: "vue", done: false}
]
}
updateTodos = (name) => {
const uuid = require('uuid');
const newTodo = {id: uuid.v1(), name: name, done: false};
this.setState({todos: [...this.state.todos, newTodo]});
}
updateTodo = (id, done) => {
const {todos} = this.state;
const newTodos = todos.map((todo) => {
if (todo.id === id) {
todo.done = !done;
return todo;
} else {
return todo;
}
});
this.setState({todos: newTodos});
}
deleteTodo = (id) => {
const {todos} = this.state;
const newTodos = todos.filter((todo) => {
return id !== todo.id
});
this.setState({todos: newTodos});
}
clearTodos = () => {
const {todos} = this.state;
const newTodos = todos.map((todo) => {
todo.done = false;
return todo;
});
this.setState({todos: newTodos});
}
checkTodos = (checked) => {
const {todos} = this.state;
const newTodos = todos.map((todo) => {
todo.done = checked;
return todo;
});
this.setState({todos: newTodos});
}
render() {
const {todos} = this.state;
return (
<div>
<Header updateTodos={this.updateTodos}/>
<List todos={todos} deleteTodo={this.deleteTodo} updateTodo={this.updateTodo}/>
<Footer todos={todos} clearTodos={this.clearTodos} checkTodos={this.checkTodos}/>
</div>
);
}
}
export default App;
Footer
import React, {Component} from 'react';
import './index.css'
class Footer extends Component {
handleClick = () => {
this.props.clearTodos();
}
handleChange = (event) => {
const checked = event.target.checked;
this.props.checkTodos(checked);
}
render() {
const {todos} = this.props;
const total = todos.length;
const doneCount = todos.reduce((pre, todo) => {
if (todo.done === true) {
pre += 1;
}
return pre;
}, 0)
return (
<div>
<input type="checkbox" onChange={this.handleChange}/>
<span>已完成{doneCount}</span>/全部{total}
<button onClick={this.handleClick}>清除已完成任务</button>
</div>
);
}
}
export default Footer;
Header
import React, {Component} from 'react';
import './index.css'
class Header extends Component {
handleChange = (event) => {
const {keyCode, target} = event;
if (keyCode !== 13 || target.value === '') {
return;
}
this.props.updateTodos(target.value);
target.value = '';
}
render() {
return (
<input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={this.handleChange}/>
);
}
}
export default Header;
List
import React, {Component} from 'react';
import Item from "../Item";
import './index.css'
class List extends Component {
render() {
const {todos} = this.props;
return (
<ul>
{todos.map((todo) => <Item {...todo} key={todo.id}
deleteTodo={this.props.deleteTodo}
updateTodo={this.props.updateTodo}/>)}
</ul>
);
}
}
export default List;
Item
import React, {Component} from 'react';
import './index.css'
class Item extends Component {
state = {mouse: false}
handleClick = (id) => {
this.props.deleteTodo(id);
}
handleCheck = (id, done) => {
this.props.updateTodo(id, done);
}
mouseE = () => {
this.setState({mouse: true});
}
mouseL = () => {
this.setState({mouse: false});
}
render() {
const {id, name, done} = this.props;
const {mouse} = this.state;
return (
<li onMouseEnter={this.mouseE} onMouseLeave={this.mouseL}
style={{backgroundColor: (mouse ? "#ddd" : "white")}}>
<input type="checkbox" onChange={() => this.handleCheck(id, done)} checked={done}/>
<span>{name}</span>
<button onClick={() => this.handleClick(id)} style={{display: (mouse ? 'block' : 'none')}}>删除</button>
</li>
);
}
}
export default Item;
这样整个逻辑代码就已经实现了,由于本人是新手,打以上代码花了差不多一天吧,虽然这次练习纯手敲并未参考,但之前已经跟着教程敲过一遍了。在练习的时候总结了一些基础知识点,如下:
- object的key是不加引号的字符串
- style中背景色关键字是backgroundColor
- array.filter()是自带return的,只需要判断一下,true就返回遍历的对象
- array.reduce(),第一个参数是function,入参有两个,第一个可认为是自定义变量,第二个是数组遍历的元素;第二个参数是function第一个入参的初始值
- 状态提升,先写事件,事件需要操作state就写好操作函数,然后在父组件中定义该操作函数,事件中操作使用props调用该函数
- input,需要根据输入值来判断的时候,不要使用onChange事件,使用键盘事件即onKeyUp/onKeyDown
总结:勤加练习,这样的题目至少要能纯手敲一遍
样式就不弄了,偷个懒