上次我们介绍了React入门的Hello World程序,还搭建了一个简单的React运行环境,今天就来写一个TodoList程序,逐步学习React开发。
注意,下面的程序是基于上篇文章中的react-dev项目的,如果没有看过,大家可以移步至:React入门之HelloWorld及环境搭建
根据React的开发思想,一切皆为模块和复合组件,所以我们要做一个TodoList,首先要对这个功能进行分析,划分模块和创建组件,最后才开始实现业务逻辑。
我们的TodoList比较简单,只是列举了几条待办事项的名称和时间,所以我们就来简单划分一下模块吧:
|- TodoList |- TodoItem |- TodoItem
可以看到,TodoList相当于一个容器,里面可以放很多TodoItem组件,接下来,我们先来实现TodoList组件,让它简单地显示一行文字,JSX文件代码如下:
// main.jsx@H_502_12@
var@H_502_12@ React = require@H_502_12@('react'@H_502_12@);
var@H_502_12@ ReactDOM = require@H_502_12@('react-dom'@H_502_12@);
var@H_502_12@ TodoList = React.createClass({
render: function@H_502_12@()@H_502_12@ {@H_502_12@
return@H_502_12@ (
<div className="todoList"@H_502_12@>
Your todo list@H_502_12@ goes here.
</div>
);
}
});
ReactDOM.render(
<TodoList/>,document.getElementById('container'@H_502_12@)
);
上面的代码先使用React.createClass定义一个TodoList组件,然后在其render方法中定义HTML模板结构,可以看到,我们定义了一个div
标签,内容就显示一行文字,非常的简单。不过需要注意的是,React规定,必须使用className
关键字来定义元素样式的class,一方面class
和浏览器中的保留字冲突,另外这是React经过诸多方面的考量决定的。如果大家想立即探个究竟,具体原因请参考:Why do I have to use className instead of class in React
然后下面的一段代码中我们使用ReactDOM.render来将<TodoList/>
渲染到DOM中去。对应的HTML和CSS如下:
<body@H_502_12@>@H_502_12@
<div@H_502_12@ id@H_502_12@="container"@H_502_12@>@H_502_12@</div@H_502_12@>@H_502_12@
<script@H_502_12@ type@H_502_12@="text/javascript"@H_502_12@ src@H_502_12@="js/main.min.js"@H_502_12@>@H_502_12@@H_502_12@</script@H_502_12@>@H_502_12@
</body@H_502_12@>@H_502_12@
* { margin@H_502_12@: 0@H_502_12@@H_502_12@@H_502_12@; padding@H_502_12@: 0@H_502_12@@H_502_12@@H_502_12@; Box-sizing@H_502_12@: border-Box@H_502_12@@H_502_12@; }@H_502_12@@H_502_12@
.todoList@H_502_12@ { width@H_502_12@: 450@H_502_12@px@H_502_12@@H_502_12@; padding@H_502_12@: 10@H_502_12@px@H_502_12@@H_502_12@; margin@H_502_12@: 10@H_502_12@px auto@H_502_12@@H_502_12@; background-color@H_502_12@: wheat@H_502_12@@H_502_12@; }@H_502_12@@H_502_12@
当我们运行之后,页面就显示出一行文字,如图:
接下来我们需要创建TodoItem组件了,假设我们有这样一份数据,表示一个todo的列表:
var@H_502_12@ todo = {id: 0@H_502_12@,name: "todo name"@H_502_12@,time: "8:00"@H_502_12@};
我们希望创建TodoItem
组件并将其添加到TodoList中,稍微修改一下代码:
var TodoList = React.createClass({
render: function() {
@H_403_176@ return@H_502_12@ (
<div@H_502_12@ className="todoList"@H_502_12@>
<TodoItem id@H_502_12@="0"@H_502_12@ name@H_502_12@="sleep"@H_502_12@ time@H_502_12@="23:00"@H_502_12@/>
<TodoItem id@H_502_12@="1"@H_502_12@ name@H_502_12@="get up"@H_502_12@ time@H_502_12@="8:00"@H_502_12@/>
</div@H_502_12@>
);
}
});
var TodoItem = React.createClass({
render: function() {
@H_403_176@ return@H_502_12@ (
<div@H_502_12@ className="todoItem"@H_502_12@>
<input type="checkBox"@H_502_12@ value={this.props.id@H_502_12@}/>
<div@H_502_12@ className="name"@H_502_12@>{this.props.name@H_502_12@}</div@H_502_12@>
<div@H_502_12@ className="time"@H_502_12@>{this.props.time@H_502_12@}</div@H_502_12@>
</div@H_502_12@>
);
}
});
可以看到,我们在TodoList中添加了两个TodoItem,并且传递了三个属性,这三个属性将会在TodoItem中使用
props`获取到。下面是TodoItem相关的样式:
.todoItem@H_502_12@ { width@H_502_12@: 400@H_502_12@px@H_502_12@@H_502_12@; height@H_502_12@: 40@H_502_12@px@H_502_12@@H_502_12@; background-color@H_502_12@: #eee@H_502_12@@H_502_12@@H_502_12@; border-radius@H_502_12@: 3@H_502_12@px@H_502_12@@H_502_12@; display@H_502_12@: flex@H_502_12@@H_502_12@; justify-content@H_502_12@: center@H_502_12@@H_502_12@; margin@H_502_12@: 10@H_502_12@px auto 0@H_502_12@@H_502_12@@H_502_12@; }@H_502_12@@H_502_12@
.todoItem@H_502_12@:first-child@H_502_12@ { margin-top@H_502_12@: 0@H_502_12@@H_502_12@@H_502_12@; }@H_502_12@@H_502_12@
.todoItem@H_502_12@ * { width@H_502_12@: 50@H_502_12@px@H_502_12@@H_502_12@; line-height@H_502_12@: 40@H_502_12@px@H_502_12@@H_502_12@; text-align@H_502_12@: center@H_502_12@@H_502_12@; }@H_502_12@@H_502_12@
.todoItem@H_502_12@ input@H_502_12@ { width@H_502_12@: 30@H_502_12@px@H_502_12@@H_502_12@; height@H_502_12@: 100@H_502_12@%@H_502_12@@H_502_12@; }@H_502_12@@H_502_12@
.todoItem@H_502_12@ .name@H_502_12@ { flex-grow@H_502_12@: 1@H_502_12@@H_502_12@@H_502_12@; text-align@H_502_12@: left@H_502_12@@H_502_12@; }@H_502_12@@H_502_12@
现在再来看看渲染出来的画面吧:
是不是像那么回事了~
但我们并不满足于此,实际生产环境也不会这样使用,我们的数据形式更像下面这样:
var mockData = [
{id@H_502_12@: 1@H_502_12@,name@H_502_12@: "report the updates to Boss"@H_502_12@,time@H_502_12@: "9:30"@H_502_12@},{id@H_502_12@: 2@H_502_12@,name@H_502_12@: "Stand-up meeting"@H_502_12@,time@H_502_12@: "10:00"@H_502_12@},{id@H_502_12@: 3@H_502_12@,name@H_502_12@: "Draw up a plan for next step"@H_502_12@,time@H_502_12@: "11:00"@H_502_12@}
];
我们可以把mockData传递到TodoList中,然后再渲染出TodoItem,再修改一下代码:
var@H_502_12@ TodoList = React.createClass({
render: function@H_502_12@()@H_502_12@ {@H_502_12@
//get the data in props,and transform,make them in the form of <TodoItem/>@H_502_12@
var@H_502_12@ todoItems = this@H_502_12@.props.data.map(function@H_502_12@(todo)@H_502_12@ {@H_502_12@
return@H_502_12@ (
//the 'key' property is required@H_502_12@
<TodoItem key={todo.id} id={todo.id} name={todo.name} time={todo.time}/>
);
});
return@H_502_12@ (
//we use {todoItems} here instead of <TodoItem/>@H_502_12@
<div className="todoList"@H_502_12@>{todoItems}</div>
);
}
});
var@H_502_12@ TodoItem = React.createClass({
render: function@H_502_12@()@H_502_12@ {@H_502_12@
return@H_502_12@ (
<div className="todoItem"@H_502_12@>
<input type="checkBox"@H_502_12@ value={this@H_502_12@.props.id}/>
<div className="name"@H_502_12@>{this@H_502_12@.props.name}</div>
<div className="time"@H_502_12@>{this@H_502_12@.props.time}</div>
</div>
);
}
});
var@H_502_12@ mockData = [
{id: 1@H_502_12@,name: "report the updates to Boss"@H_502_12@,time: "9:30"@H_502_12@},{id: 2@H_502_12@,name: "Stand-up meeting"@H_502_12@,time: "10:00"@H_502_12@},{id: 3@H_502_12@,name: "Draw up a plan for next step"@H_502_12@,time: "11:00"@H_502_12@}
];
ReactDOM.render(
//adding a ‘data’ property referring to mockData@H_502_12@
<TodoList data={mockData}/>,document.getElementById('container'@H_502_12@)
);
上面的代码中我们直接把mockData传递进TodoList组件,然后在TodoList的render方法中要用到map函数对数据进行处理,将其映射为多个TodoItem组件组成的集合。注意map函数中的组件一定要带有一个key
属性,否则会抛出异常。再来看一下现在的视图吧:
实际开发中,我们的数据一开始并不存在,需要在运行过程中通过网络加载,所以我们的程序还不满足实际需要,还要再改进一下代码,最终的文件如下:
// main.jsx@H_502_12@
var@H_502_12@ React = require('react'@H_502_12@);
var@H_502_12@ ReactDOM = require('react-dom'@H_502_12@);
var@H_502_12@ TodoList = React.createClass({
getInitialState: function@H_502_12@()@H_502_12@ {@H_502_12@
//set the initial state with data@H_502_12@
return@H_502_12@ {data: []};
},componentDidMount: function@H_502_12@()@H_502_12@ {@H_502_12@
console.log('in real dev we get data via:'@H_502_12@,this@H_502_12@.props.url);
//simulate the process of obtaining data@H_502_12@
var@H_502_12@ networkTime = 1000@H_502_12@;
var@H_502_12@ mockData = [
{id: 1@H_502_12@,time: "11:00"@H_502_12@}
];
setTimeout(function@H_502_12@()@H_502_12@ {@H_502_12@
//set state to re-render the view@H_502_12@
this@H_502_12@.setState({data: mockData});
}.bind(this@H_502_12@),networkTime);
},render: function@H_502_12@()@H_502_12@ {@H_502_12@
//remember to use 'this.state.data' rather than 'this.props.data'@H_502_12@
var@H_502_12@ todoItems = this@H_502_12@.state.data.map(function@H_502_12@(todo)@H_502_12@ {@H_502_12@
return@H_502_12@ (
<TodoItem key={todo.id} id={todo.id} name={todo.name} time={todo.time}/>
);
});
return@H_502_12@ (
<div className="todoList"@H_502_12@>{todoItems}</div>
);
}
});
var@H_502_12@ TodoItem = React.createClass({
render: function@H_502_12@()@H_502_12@ {@H_502_12@
return@H_502_12@ (
<div className="todoItem"@H_502_12@>
<input type="checkBox"@H_502_12@ value={this@H_502_12@.props.id}/>
<div className="name"@H_502_12@>{this@H_502_12@.props.name}</div>
<div className="time"@H_502_12@>{this@H_502_12@.props.time}</div>
</div>
);
}
});
var@H_502_12@ url = 'http://localhost:8080/todos'@H_502_12@;
ReactDOM.render(
//replace the 'data' with 'url'@H_502_12@
<TodoList url={url}/>,document.getElementById('container'@H_502_12@)
);
这一次我们给TodoList提供了一个url用于通过网络获取真正的数据,在TodoList里面,需要给它一个初始值,这是通过声明getInitialState
函数来实现的,这个函数负责做一些组件初始化的工作,我们初始化data为一个空数组,后面可以通过state.data
来访问,不再通过props
来获取数据了。此外,我们也声明了一个componentDidMount
函数,这个是干嘛的呢,它表示当前组件已被挂载成功,在render
函数执行之后做一些业务逻辑操作,我们就再这个函数里面加载真正需要的数据。注意,此示例中,在获取到数据之后,要记得调用setState
函数将数据替换为最新的集合,这样React会重新渲染视图。
刚学习React的同学们也赶紧跑一下这个小程序吧。