大家好,在前面的几篇文章中,我们已经初步学习了组件的使用方法,但是这些内容只是开始,关于组件的内容还是比较多,比如在组件生命周期内提供了处理数据的方法,在特定的应用场景更新我们的应用,这对我们开发十分重要,因此我们有必要深入学习。在接下来的几篇文章里,我将通过实例的方式和大家一起学习组件生命周期相关的主要方法,本篇文章我们将通过一个待办事项的例子(Todo list)来深入学习如何使用 componentWillMount 函数。
componentWillMount 函数的触发时机是在组件将要装载,在组件render之前调用。与其相对的是另外一个函数 componentDidMount,在组件加载完成, render之后调用,关于这个函数的介绍,将会在下一篇文章进行介绍。
有一点需要说明的是,componentWillMount 函数将会在未来 v17.0 版本中被移除,在目前最新的 v16.12 版本中还能够使用,但是你会在浏览器控制台里收到如下的信息警告:
如何修复以上的错误问题,我们可以使用 UNSAFE_componentWillMount 方法来替代 componentWillMount。虽然在未来的版本中即将废弃,但是不妨碍我们现在使用,还是有必要了解下的。
1、首先我们来看看本示例完成后的效果,如下图所示,首先界面初始化一些历史任务信息,我们可以进行更新完成状态同时可以进行删除,输入框用于添加新任务
2、接下来基于上篇文章的项目代码,我们在 components 目录创建一个 Todo 的文件夹,然后新建 Todo.js、Todo.css 文件 ,以下为 Todo 组件的基本结构代码:
import React, { Component } from 'react';
import './Todo.css';
class Todo extends Component {
constructor() {
super();
}
componentWillMount() {
}
render() {
return (
<div className="Todo">
<h1>New Task:</h1>
</div>
);
}
}
export default Todo;
// File: src/components/Todo/Todo.js”
3、然后我们在构造函数中初始化本地的数据状态,对于任务列表 Todo list,我们需要定义任务(task)及列表数组(items )
constructor() {
super();
// Initial state...
this.state = {
task: '',
items: []
};
}
4、接下来我们开始定义 componentWillMount() 方法,这个方法我们在文章的开头提及过了,在组件被渲染之前调用,这个例子,我们在组件 render 之前初始化我们的任务数据,由于在目前的版本使用 componentWillMount() 有警告提示,在未来的17版本中将会被删除,在这里我们使用了 UNSAFE_componentWillMount() 进行了替代,示例代码如下:
UNSAFE_componentWillMount() {
this.setState({
items:[
{
id: uuidv4(),
task: 'Pay the rent',
completed: false
},
{
id: uuidv4(),
task: 'Go to the gym',
completed: false
},
{
id:uuidv4(),
task:'Do my homework',
completed:false
}
]
});
}
5、在上述初始化数据时,我们使用了 uuidv4() 方法,这里我们需要安装相关的第三方依赖包,安装命令如下:
npm install uuid
6、接下来我们在文件的开头引入第三方依赖:
import uuidv4 from 'uuid/v4';
7、定义完默认数据状态后,我们需要定义组件的外观,即 render() 渲染相关的内容——我们的任务列表(Todo list)示例代码如下:
render() {
return (
<div className="Todo">
<h1>New Task:</h1>
<form onSubmit={this.handleOnSubmit}>
<input
value={this.state.task}
onChange={this.handleOnChange}
/>
</form>
<List
items={this.state.items}
markAsCompleted={this.markAsCompleted}
removeTask={this.removeTask}
/>
</div>
);
}
8、上述的组件 JSX 分为两个部分,第一部分是输入表单,关联我们的数据状态 this.state.task 部分,当用户提交表单时,我们保存新添加的任务,到任务列表数组中。第二部分是调用任务列表组件,我们传递了 items 数组 和 操作任务完成状态方法(markAsCompleted)和 删除任务方法(removeTask)这三个属性。
9、接下来我们来看看 handleOnChange() 方法,这个方法和我们的数据状态task数据关联,示例代码如下:
handleOnChange = e => {
const { target: { value } } = e;
// Updating our task state with the input value...
this.setState({
task: value
});
}
10、接着我们来继续定义我们的表单提交方法 handleOnSubmit(), 这个方法主要是将我们新添加的数据保存到任务数组中,示例代码如下:
handleOnSubmit = e =>{
e.preventDefault();
if (this.state.task.trim() !==''){
this.setState({
task:'',
items:[
...this.state.items,
{
id: uuidv4(),
task: this.state.task,
complete:false
}
]
})
}
};
11、markAsCompleted() 方法接收一个任务 ID 标识参数,这个方法用于标识任务已完成,其逻辑主要先查找到对应 ID 的任务,将其更新已完成,然后刷新本地的数据状态,示例代码如下:
markAsCompleted = id => {
const foundTask = this.state.items.find(
task => task.id ===id
);
foundTask.completed = true;
this.setState({
...this.state.items,
...foundTask
});
};
12、接下来我们来定义任务删除方法 removeTask(),这个方法同 markAsCompleted() 一样,接收 ID 参数,用于移除对应任务,示例代码如下:
removeTask = id => {
const filteredTasks = this.state.items.filter(
task => task.id !== id
);
this.setState({
items:filteredTasks
});
};
13、这样我们的 Todo 组件就完成了,让我们将代码组合在一起,完整的代码如下:
import React, {Component} from "react";
import './Todo.css'
import uuidv4 from 'uuid/v4'
import './List'
import List from "./List";
class Todo extends Component{
constructor() {
super();
this.state={
task:'',
items:[]
};
}
handleOnChange = e => {
const { target: { value } } = e;
this.setState({
task: value
});
};
handleOnSubmit = e =>{
e.preventDefault();
if (this.state.task.trim() !==''){
this.setState({
task:'',
items:[
...this.state.items,
{
id: uuidv4(),
task: this.state.task,
complete:false
}
]
})
}
};
markAsCompleted = id => {
const foundTask = this.state.items.find(
task => task.id ===id
);
foundTask.completed = true;
this.setState({
...this.state.items,
...foundTask
});
};
removeTask = id => {
const filteredTasks = this.state.items.filter(
task => task.id !== id
);
this.setState({
items:filteredTasks
});
};
UNSAFE_componentWillMount() {
this.setState({
items:[
{
id: uuidv4(),
task: 'Pay the rent',
completed: false
},
{
id: uuidv4(),
task: 'Go to the gym',
completed: false
},
{
id:uuidv4(),
task:'Do my homework',
completed:false
}
]
});
}
render() {
return (
<div className="Todo">
<h1>New Task:</h1>
<form onSubmit={ this.handleOnSubmit }>
<input
value={ this.state.task }
onChange={ this.handleOnChange }
/>
</form>
<List
items={this.state.items}
markAscompleted={this.markAsCompleted}
removeTask={this.removeTask}
/>
</div>
);
}
}
export default Todo;
// File: src/components/Todo/Todo.js
14、然后我们来完成我们的列表展示组件,我们在 todo 目录下创建 List.js 文件,示例代码如下,代码比较简单,这里就不过介绍了。
import React from "react";
const List = props => (
<ul>
{props.items.map( (item,key) => (
<li
key={ key }
className={`${item.completed ? 'completed' : 'pending'}`}
>
{ item.task }
<div className="actions">
<span
className={item.completed ? 'hide' : 'done' }
onClick={ () => props.markAscompleted(item.id)}
>
<i className="fa fa-check"></i>
</span>
<span
className="trash"
onClick={ ()=>props.removeTask(item.id) }
>
<i className="fa fa-trash"></i>
</span>
</div>
</li>
))}
</ul>
);
export default List;
// File: src/components/Todo/List.js
15、在我们调用 map 函数渲染列表时,别忘记 key 这个属性加入到列表元素上,否则浏览器控制台将会收到如下的警告:
16、你可能注意到,在我们的界面中,我们引入了一些 Font Awesome 图标,我们需要在我们 index.html 文件引入图标的 CSS 文件,部分示例代码如下:
<head>
<title>React App</title>
<link
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
rel="stylesheet"
/>
</head>
/* File: public/index.html */
17、最后我们来定义我们的组件样式,代码部分如下:
.Todo {
background-color: #f5f5f5;
border-radius: 4px;
border: 1px solid #e3e3e3;
box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
margin-bottom: 20px;
margin: 50px auto;
min-height: 20px;
padding: 19px;
text-align: left;
width: 70%;
}
.Todo ul {
margin: 20px 0px;
padding: 0;
list-style: none;
}
.Todo ul li {
background-color: #fff;
border: 1px solid #ddd;
display: flex;
justify-content: space-between;
margin-bottom: -1px;
padding: 10px 15px;
position: relative;
}
.Todo ul li .hide {
border: 1px solid red;
visibility: hidden;
}
.Todo ul li.completed {
background-color: #dff0d8;
}
.Todo ul li .actions {
display: flex;
justify-content: space-between;
width: 40px;
}
.Todo ul li span {
cursor: pointer;
}
.Todo ul li .done {
color: #79c41d;
display: block;
}
.Todo ul li .trash {
color: #c41d1d;
display: block;
}
.Todo form input {
background-color: #fff;
border-radius: 4px;
border: 1px solid #ccc;
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
color: #555;
font-size: 14px;
height: 34px;
line-height: 34px;
padding: 6px 12px;
width: 40%;
}
.Todo form button {
background: #2ba6cb;
border: 1px solid #1e728c;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset;
color: white;
cursor: pointer;
display: block;
font-size: 14px;
font-weight: bold;
line-height: 1;
margin: 20px auto;
padding: 10px 20px 11px;
position: relative;
text-align: center;
text-decoration: none;
}
/* File: src/components/Todo/Todo.css */
18、还有最后一步,我们别忘记修改我们的 App 组件,否则我们 Todo 核心组件将无法渲染,完成后代码如下:
import React from 'react';
import './App.css';
// 引入布局相关的组件
import Header from "../shared/components/Header/Header";
import Content from "../shared/components/Content/Content";
import Footer from "../shared/components/Footer/Footer";
import Todo from "./Todo/Todo";
function App() {
return (
<div className="App">
<Header title="Todo List"/>
<Content>
<Todo/>
</Content>
<Footer/>
</div>
);
}
export default App;
// File: src/components/App.js
19、如果你按照上述顺序正确完成后,接下来在控制台输入 npm run start 命令, 你将会看到如下界面:
本节的内容就给大家介绍到这里,想必大家通过这个练习已经熟悉了 componentWillMount 函数的用法,在下一篇文章里我们继续通过做实例的形式,学习另外一个与其对应的方法 componentDidMount(),敬请期待...
React基础相关文章
专注分享当下最实用的前端技术。关注前端达人,与达人一起学习进步!
长按关注"前端达人"