初学React,撸一个TodoList熟悉熟悉基本语法,只有最简单最简单的功能。
如上图所示,是一个最简单的TodoList的样子了,我们应该怎样把它拆成一个个的组件呢?
在之前看来,可能就是这样一个HTML结构:
<div> <h1></h1> <div> <ul> <li></li> <li></li> <li></li> </ul> </div> <div> <input/> <button>保存</button> </div> </div>@H_301_12@
React的核心思想是:封装组件。
我们也可以按照这个思路来进行组件设计
组件设计
从小到大,从内到外 ~
我是这样进行设计的。
除去按钮,input这些之外,<li></li>
是HTML中最小的元素,我们可以先每一个<li></li>
当成是一个最小的组件,也就是图中橙色框的部分,它对应着每一条内容,我们先把它命名为TodoItem
吧。
<li></li>
的父级元素是<ul></ul>
,那就把它看作一个组件呗,图中位于上方的蓝色部分,命名为TodoList
。
恩,此时Todo内容的展示组件已经是够的了,我们再来加一个添加Todo内容的组件AddTodoItem
吧,命名貌似有点丑- -,图中位于下方的蓝色部分。
最后就是最外层的红色部分了,它就是整个app的主体部分,包含着其它小组件,命名为TodoBox
。
ok,暂时就这几个小组件 ~
然我们开始愉快的撸代码吧 ~
代码部分
Index
先看看入口程序,很简单。
var React = require('react'); var ReactDOM = require('react-dom'); import TodoBox from './components/todoBox'; import './../css/index.css'; export default class Index extends React.Component { constructor(){ super(); }; render() { return ( <TodoBox /> ); } } ReactDOM.render(<Index/>,document.getElementById("example"))
TodoItem
让我们想想啊,对于每一条内容来说,需要什么呢?
那不是太简单了 ~
<li> <input type="checkBox"/>找工作啊找工作啊 <button>删除</button> </li>
不不不,我们现在是在写React
,要这样:
import React from 'react'; import {Row,Col,CheckBox,Button} from 'antd'; export default class TodoItem extends React.Component { constructor(props) { super(props) this.toggleComplete = this.toggleComplete.bind(this) this.deleteTask = this.deleteTask.bind(this) } toggleComplete() { this.props.toggleComplete(this.props.taskId) } deleteTask() { this.props.deleteTask(this.props.taskId) } render() { let task = this.props.task let itemChecked if (this.props.complete === "true") { task = <del>{task}</del> itemChecked = true } else { itemChecked = false } return ( <li className="list-group-item"> <Row> <Col span={12}> <CheckBox checked={itemChecked} onChange={this.toggleComplete}/> {task} </Col> <Col span={12}> <Button type="danger" className="pull-right" onClick={this.deleteTask}>删除</Button> </Col> </Row> </li> ) } }
import {Row,Button} from 'antd'
是引入Ant Design。
我们采用 React 封装了一套 Ant Design 的组件库,也欢迎社区其他框架的实现版本。
引入这个之后,我们可以直接使用一些简单的UI组件,比如Row
,Col
,CheckBox
,Button
等,我们可以更加注重业务逻辑的实现。
TodoList
接下来就是拿一个<ul></ul>
把item包起来呗:
import React from 'react'; import TodoItem from './todoitem'; export default class TodoList extends React.Component{ constructor(props) { super(props); } render(){ var taskList=this.props.data.map(listItem=> <TodoItem taskId={listItem.id} key={listItem.id} task={listItem.task} complete={listItem.complete} toggleComplete={this.props.toggleComplete} deleteTask={this.props.deleteTask}/> ) return( <ul className="list-group"> {taskList} </ul> ) } }
AddTodoItem
添加内容这个组件也比较简单,就只需要一个input
和一个button
即可:
import React from 'react'; import ReactDOM from 'react-dom'; import {Row,Form,Input,Button,notification } from 'antd'; export default class AddTodoItem extends React.Component { constructor(props) { super(props) this.saveNewItem = this.saveNewItem.bind(this) } saveNewItem(e) { e.preventDefault() let element = ReactDOM.findDOMNode(this.refs.newItem) let task = element.value if (!task) { notification.open({ description: 'Todo内容不得为空!',}); } else { this.props.saveNewItem(task) element.value = "" } } render() { return ( <div className="addtodoitem"> <Form.Item> <label htmlFor="newItem"></label> <Input id="newItem" ref="newItem" type="text" placeholder="吃饭睡觉打豆豆~"></Input> <Button type="primary" className="pull-right" onClick={this.saveNewItem}>保存</Button> </Form.Item> </div> ) } }
TodoBox
我们的小组件已经都实现了,拿一个大Box
包起来呗 ~
import React from 'react'; import TodoList from './todolist'; import AddTodoItem from './addtodoitem'; import {Button,Icon,Row,Col} from 'antd'; export default class TodoBox extends React.Component { constructor(props) { super(props) this.state = { data: [ { "id": "1","task": "做一个TodoList Demo","complete": "false" },{ "id": "2","task": "学习ES6",{ "id": "3","task": "Hello React","complete": "true" },{ "id": "4","task": "找工作","complete": "false" } ] } this.handleToggleComplete = this.handleToggleComplete.bind(this); this.handleTaskDelete = this.handleTaskDelete.bind(this); this.handleAddTodoItem = this.handleAddTodoItem.bind(this); } generateGUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(c) { var r = Math.random() * 16 | 0,v = c == 'x' ? r : (r & 0x3 | 0x8) return v.toString(16) }) } handleToggleComplete(taskId) { let data = this.state.data; for (let item of data) { if (item.id === taskId) { item.complete = item.complete === "true" ? "false" : "true" } } this.setState({data}) } handleTaskDelete(taskId) { let data = this.state.data data = data.filter(task => task.id !== taskId) this.setState({data}) } handleAddTodoItem(task) { let newItem = { id: this.generateGUID(),task,complete: "false" } let data = this.state.data data = data.concat([newItem]) this.setState({data}) } render() { return ( <div> <div className="well"> <h1 className="text-center">React TodoList</h1> <TodoList data={this.state.data} toggleComplete={this.handleToggleComplete} deleteTask={this.handleTaskDelete}/> <AddTodoItem saveNewItem={this.handleAddTodoItem}/> </div> <Row> <Col span={12}></Col> <Col span={12}> <Button className="pull-left"><Icon type="user"/> <a href="http://axuebin.com">薛彬</a> </Button> <Button className="pull-right"><Icon type="github"/> <a href="https://github.com/axuebin">axuebin</a> </Button> </Col> </Row> </div> ) } }
注意: