React开发的ToDoList,麻雀虽小,五脏俱全。基本涉及到React的基础知识点,这是看过官网的文档后写的项目,并总结了React基础中需要注意的地方,不足之处,敬请指出和补充。
最终效果:
创建React项目
- 安装。需要安装
node.js
// 国内使用 npm 速度很慢,你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ npm config set registry https://registry.npm.taobao.org
- 使用create-react-app创建项目
$ cnpm install -g create-react-app
$ create-react-app todolist
$ cd todolist/
$ npm start
项目结构
整体的项目结构如下:
这里新建了一个todolist目录,用于todolist各组件的开发。
执行npm start
后就可以进行开发了,至于React的基础用法,官网文档看过一边后就可以进行开发了,不懂的边开发边看文档,这样理解起来比较深刻。
整体组件开发思路
这是最终的效果:
涉及的点:
ToDoList
import React, { Component, Fragment } from 'react'
import TodoItem from './TodoItem'
import './style.css'
class ToDoList extends Component {
constructor(props) {
super(props)
this.state = {
mapList: [
{
key: '上班打怪',
value: '周末愉快'
}
],
inputValue: ''
}
// 在 JavaScript 中,class 的方法默认不会绑定 this。
this.btnInputValue = this.btnInputValue.bind(this)
this.btnAdd = this.btnAdd.bind(this)
this.btnDelete = this.btnDelete.bind(this)
this.btnKeyPress = this.btnKeyPress.bind(this)
}
render() {
return (
<Fragment>
{/* header */}
<header>
<div className="header-left">
<span className="title">ToDoList</span>
</div>
<div className="header-right">
<input placeholder="添加ToDo" value={ this.state.inputValue } onKeyPress={this.btnKeyPress} onChange = { this.btnInputValue } />
<button onClick={ this.btnAdd }>提交</button>
</div>
</header>
{/* 列表 */}
<section>
{ this.getTodoItem() }
</section>
</Fragment>
)
}
getTodoItem() {
return this.state.mapList.map((item, index) => {
return (
<TodoItem
content={item}
index={index}
key={index}
deleteItem={this.btnDelete} />
)
})
}
btnInputValue(event) {
const value = event.target.value
// setState的更新可能是异步的,采用es6的箭头函数
this.setState(() => ({ inputValue: value }))
}
btnAdd(e) {
// setState的更新可能是异步的,采用es6的箭头函数
this.setState((prevState) => ({
mapList: [ { key: prevState.inputValue, value: prevState.inputValue }, ...prevState.mapList],
inputValue: ''
}))
}
btnDelete(index) {
// setState的更新可能是异步的,采用es6的箭头函数
this.setState((prevState) => {
const mapList = [...prevState.mapList]
mapList.splice(index,1)
return { mapList }
})
}
btnKeyPress(e) {
// 判断键盘事件是否为enter
if(e.nativeEvent.keyCode === 13) {
this.btnAdd(e)
}
}
}
export default ToDoList
ToDoItem
import React, { Component } from 'react'
import './todoitem.css'
class TodoItem extends Component {
constructor(props) {
super(props)
// class 默认不绑定this
this.handleClick = this.handleClick.bind(this)
}
render() {
const { content } = this.props
return (
<div className="item">
<div className="left">{ content.key }</div>
<div className="right" onClick={this.handleClick}>-</div>
</div>)
}
handleClick() {
// 采用es6的写法
const { content, index, deleteItem } = this.props
deleteItem(index)
}
}
export default TodoItem
总结
react.js
细节
- 尽量不要用index做key(为什么)
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。
- 方法需要绑定this(为什么)
在 JavaScript 中,class 的方法默认不会绑定 this。
- state只能用setState改变
- 对于数组的新增可以使用展开运算符
[...this.state.mapList, a, b]
JSPX
细节
- 样式应该用
className
代替class
dangerouslySetInnerHTML
不会被转义。JSX
默认是转义的。for
应该用htmlFor
代替- Fragment占位
父子组件通信
- 父组件传值给子组件用属性,子组件接收用
this.props
,子组件内部状态维护用state
- 子组件使用父组件的方法,直接用属性传递方法
state
的可能更新可能是异步的
- react中更新状态只能用setState
- 出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。
- 用es6的写法能避免不小心改变了state的状态。
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// 要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 // props 做为第二个参数:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
- State 的更新会被合并.当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。