标签:footer 工作 ebe 参考 genius 功能 detail lang lan

今天我们就使用 react 来实现一个简易版的 todolist ,我们可以使用这个 demo 进行 list 的增删改差,实际效果如上图所示。大家可以clone下来查看:react-todolist
这篇文章我们就不使用 redux,因为这个 demo 本身比较简单,不需要通过 redux 来管理我们的状态。
redux中也有非常有名的一句话叫做:
"如果你不知道是否需要 Redux,那就是不需要它。"
我们废话不多说,直接进入正题。
.
├── app // 开发的文件夹,组件放在这个文件夹中
│ ├── components // 项目的组件
│ │ ├── App.js // 最外层包含下面组件的总组件
│ │ ├── AppFooter.js // App的三个筛选按钮的组件
│ │ ├── AppForm.js // 添加list的form
│ │ ├── AppList.js // 显示list数据的智能组件
│ │ └── AppTodos.js // 显示list的木偶组件
├── css // 放css文件的地方。
│ ├── semantic.css // 我们的文件用到了semantic.css,
├── node_modules // 第三方的依赖
├── .babelrc // babel配置文件
├── .gitignore // git上传时忽略的文件
├── bundle.js // webpack build之后的文件
├── index.html // 项目的模版文件
├── main.js // 项目的入口文件
├── webpack.config.js // webpack配置文件
├── README.md // readme文件
└── package.json // 当前整一个项目的依赖
1,首先我们新建一个todolist文件夹,根据我的目录结构建好相应的文件,如果大家嫌麻烦,大家可以clone我的项目,然后看着我的代码,我会一一进行说明的。package.json我们可以自己创建。
$ mkdir todolist
$ cd todolist
2,建立package.json文件
npm init
3,安装相应的依赖,我先解释一下这些依赖的作用
首先安装Babel,Babel 是一个 JavaScript 编译器,他可以将es6或者es7的语法转化为浏览器能识别的javascript。
npm install babel-cli babel-core --save-dev
其次安装我们的主角,react
npm install react react-dom --save-dev
安装webpack,打包工具;和webpack-dev-server,用于来给我们开启一个服务的。
npm install webpack webpack-dev-server --save-dev
安装loader打包,通过使用不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件,对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。
大家想了解更多的webpack的内容,可以参考webpack中文文档
npm install css-loader babel-loader style-loader --save-dev
然后我们在webpack.config.js中引用这些依赖,具体的格参数的意思,大家可以参考webpack各文档说明,我下面也会简单的注释一下。
module.exports = {
entry: ‘./main.js‘, // webpack打包的入口文件
output: {
filename: ‘./bundle.js‘ // 输出之后的文件名
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: ‘babel-loader‘ // babel的loader,jsx文件使用babel-loader处理
}, {
test: /\.css$/,
exclude: /node_modules/,
loader: ‘style!css‘ // css和styleloader,对css后缀的文件进行处理
}
]
},
devtool: ‘cheap-source-map‘,
}
同时要让我们的 babel 能在 react中生效,同时支持es6,我们需要安装下面的插件
npm install babel-preset-es2015 babel-preset-react babel-preset-stage-0 --save-dev
安装完依赖后,我们在.babelrc文件中引入这几个依赖
{
"presets": ["es2015","react",‘stage-0‘]
}
其次为了当我们每次添加list的时候有一个唯一的id,我们使用uuid
npm install uuid --save-dev
?
编写模版文件index.html
在这个模版文件里面,我们引入 semantic.css 文件,然后建立一个 id=app 的 <div> 为了我们后续的 react 操作。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-todolist</title>
<link rel="stylesheet" type="text/css" href="/css/semantic.css">
<style>
.active { color: red }
.line{
display: inline-block;
border-bottom: 1px solid #222222;
width:100px;
position: absolute;
left:0;
top:7px;
}
.ui.comments .comment{
padding:0;
margin:2em 0 0;
}
</style>
</head>
<body>
<div class="ui container" style="padding:30px;">
<div id="app"></div>
</div>
<script src="./bundle.js"></script>
</body>
</html>
编写入口文件main.js
这边的data是我们的模拟数据,将其传入到<App/>组件,在子组件中可以通过props.data的方法获取 data。对于react的基础知识,大家可以参考来自一位react新手的react入门须知。
import React from ‘react‘
import ReactDOM from ‘react-dom‘
import App from ‘./app/component/App‘
let data = [
{id: 0, text: ‘天气不错哦!!!‘, complete: false},
{id: 1, text: ‘天气不错哦!!!‘, complete: false},
{id: 2, text: ‘出去玩啊!!!‘, complete: true},
]
ReactDOM.render(
<App data={data}/>,
document.getElementById(‘app‘)
)
编写component里面的组件
App.js
这个组件我们可以认为是一个容器组件,我们会把AppForm、AppList、AppFooter放在这个组件中。
import React from ‘react‘
import AppList from ‘./AppList.js‘
import AppForm from ‘./AppForm.js‘
import AppFooter from ‘./AppFooter.js‘
class App extends React.Component {
state = {
choosevalue : 1,
data: this.props.data
}
render () {
const { data } = this.state;
return (
<div className=‘ui comments‘>
<h1>My Todo with React</h1>
<div className=‘ui divider‘></div>
<AppForm />
<AppList data={data}/>
<AppFooter />
</div>
)
}
}
export default App;
AppForm.js
这个组件是我们添加 list 用的一个form 组件,其中下面的styles这个对象那个也是jsx中申明样式的一种方式,我们还可以使用className来添加样式名字。
import React from ‘react‘;
import uuid from ‘uuid‘;
var styles = {
‘title‘: {
width: 200,
display: ‘inline-block‘,
marginRight: 10,
verticalAlign: ‘top‘
}
}
class AppForm extends React.Component {
render () {
return (
<form className=‘ui reply form‘>
<div className=‘field‘ style={styles.title}>
<input type=‘text‘ placeholder=‘TODO‘ ref=‘text‘ />
</div>
<button type=‘submit‘ className=‘ui blue button‘>
添加
</button>
</form>
)
}
}
export default AppForm;
AppList.js
这个组件是我们在react中常说的智能组件,得到数据lists后通过 map 方法遍历数据,然后进行渲染。这里的map方法是用到了es6中的解构赋值,大家可以参考react新手必须懂得es6的基础知识,然后将值一一传递到子组件中去。
import React from ‘react‘
import AppTodos from ‘./AppTodos‘
class AppList extends React.Component {
render () {
const a = this.props.data.map(({ id, text, complete }, index) => {
return
<AppTodos
key={index}
id={id}
text={text}
complete={complete}
/>
})
return (
<div> { a } </div>
)
}
}
export default AppList;
AppTodos.js
这个组件是我们在react中常说的木偶组件,就是得到数据渲染组件。
import React from ‘react‘
var styles = {
‘title‘: {
paddingLeft: ‘20px‘,
paddingRight: ‘50px‘,
position: ‘relative‘
},
‘delete‘: {
marginLeft: ‘20px‘,
marginRight: ‘50px‘
}
}
class AppTodos extends React.Component {
render () {
return (
<div className=‘comment‘>
<div className=‘content‘>
<span
className=‘author‘
style={styles.title}
>
{this.props.text}
<span
className={this.props.complete ? ‘line‘ : ‘‘}
/>
</span>
<span className=‘author‘
style={styles.title}>
{this.props.complete ? ‘已完成‘ : ‘未完成‘}
</span>
<span className=‘author‘>{this.props.id}</span>
<span className=‘ui blue button‘
style={styles.delete} >
删除
</span>
</div>
</div>
)
}
}
export default AppTodos;
AppFooter.js
这个组件就是下面的三个按钮全部、未完成、已完成。
import React from ‘react‘
var styles = {
‘title‘: {
marginRight: 10,
fontSize: 20
},
‘top‘: {
marginTop: 20
}
}
class AppFooter extends React.Component {
render () {
return (
<div>
<h2 style={styles.top}>show</h2>
<button
type=‘submit‘
style={styles.top}
className=‘ui blue button‘
value=‘1‘
ref=‘all‘
>
全部
</button>
<button
type=‘submit‘
style={styles.top}
className=‘ui blue button‘
value=‘2‘
ref=‘active‘
>
还未完成
</button>
<button
type=‘submit‘
style={styles.top}
className=‘ui blue button‘
value=‘3‘
ref=‘complete‘
>
已完成
</button>
</div>
)
}
}
export default AppFooter;
然后我们在命令行输入,会开启一个服务。
npm run server
?
打开浏览器,输入http://localhost:8080,可看到:

list的添加操作首先理一下流程
首先在form输入待办事情,点击添加触发一个handleSubmit点击函数,但是我们的data是通过<App />组件来分发的,而list是组件AppList渲染的。这里涉及到了从子组件传递值给父组件,其实也很简单,就从父组件中传一个函数给子组件,子组件将值通过函数再传递出去,大家可以参考react父子组件间的交流。
在组件App.js中,我们加入一个OnAddTodoItem函数,并传入到AppForm组件中,我们新建函数中将传进来的newItem通过concat()现在的data,然后更新state。
...
OnAddTodoItem (newItem) {
let newdata = this.state.data.concat(newItem);
this.setState({data : newdata});
}
render () {
const { data } = this.state;
return (
<div className=‘ui comments‘>
<h1>My Todo with React</h1>
<div className=‘ui divider‘></div>
<AppForm
AddTodoItem={this.OnAddTodoItem.bind(this)} />
<AppList
data={data}/>
<AppFooter />
</div>
)
}
}
export default App;
在组件AppForm.js中,我们加入一个handleSubmit函数,并在form表单添加一个onClick函数,将用户输入的数据,通过uuid生成的id、输入的text、以及是否完成false。通过函数传递给父组件。
...
handleSubmit (event) {
event.preventDefault()
let text = this.refs.text.value
if (!text.trim()) {
alert("Input can‘t be null")
return
}
let id = uuid();
this.props.AddTodoItem({id,text,complete:false});
}
render () {
return (
<form
className=‘ui reply form‘
onSubmit={this.handleSubmit.bind(this)}>
<div
className=‘field‘
style={styles.title}>
<input type=‘text‘ placeholder=‘TODO‘ ref=‘text‘ />
</div>
<button type=‘submit‘ className=‘ui blue button‘>
添加
</button>
</form>
)
}
}
export default AppForm;
你可以看到如下效果:

首先里一下流程
我们给下面的三个按钮设置了不同的value,1代表全部、2代表未完成、3代表已完成,然后我们根据相应的value,展示相应的list,给三个按钮分别加上handleAll、handleActive、handleComplete三个方法,在onClick时触发。
App.js
添加一个加上choosevalue的state,默认为1,即全选,同时将其传入到<AppList/>中去,同时添加chooseValue的方法,然后传入到AppFooter组件中去。
...
state = {
choosevalue : 1,
data: this.props.data
}
ChooseValue (id) {
this.setState({choosevalue:id});
}
...
<AppList
data={this.state.data}
choosevalue={this.state.choosevalue}
/>
<AppFooter
SubmitChooseValue={this.ChooseValue.bind(this)}
/>
AppFooter.js
...
handleAll () {
let all = this.refs.all.value;
this.props.SubmitChooseValue(all);
}
handleActive () {
let active = this.refs.active.value;
this.props.SubmitChooseValue(active);
}
handleComplete () {
let complete = this.refs.complete.value
this.props.SubmitChooseValue(complete);
}
render () {
return (
<div>
<h2 style={styles.top}>show</h2>
<button
type=‘submit‘
style={styles.top}
className=‘ui blue button‘
value=‘1‘
ref=‘all‘
onClick={this.handleAll.bind(this)}
>
全部
</button>
<button
type=‘submit‘
style={styles.top}
className=‘ui blue button‘
value=‘2‘
ref=‘active‘
onClick={this.handleActive.bind(this)}
>
还未完成
</button>
<button
type=‘submit‘
style={styles.top}
className=‘ui blue button‘
value=‘3‘
ref=‘complete‘
onClick={this.handleComplete.bind(this)}
>
已完成
</button>
</div>
)
}
}
export default AppFooter;
AppList.js
根据value渲染相应的List
...
class AppList extends React.Component {
render () {
var value = this.props.choosevalue;
const a = this.props.data.map(({ id, text, complete }, index) => {
if (value == ‘1‘) {
return <AppTodos
key={index}
id={id}
text={text}
complete={complete}
/>
}
if (value == ‘2‘ && complete === false) {
return <AppTodos
key={index}
id={id}
text={text}
complete={complete}
/>
}
if (value == ‘3‘ && complete === true) {
return <AppTodos
key={index}
id={id}
text={text}
complete={complete}
/>
}
})
return (
<div> { a } </div>
)
}
}
export default AppList;
?
你可以看到如下效果:

list状态和删除list的功能首先里一下流程
修改list的状态,其实就是我们找到相应id的list,然后将其的complete改为true就可以了。
删除list时,就是我们找到相应id的list,我们给他添加一个deleteFlag属性,在AppList渲染的时候,我门判断是否有deleteFlag来惊醒渲染。
App.js
添加两个方法AllChangeComplete:改变状态;AllOnDeleteItem:删除list的方法。然后通过组件传入到相应的子组件。
...
AllChangeComplete (id) {
let newdata = this.state.data.map((item,index) => {
if(item.id === id) {
item.complete = !item.complete;
}
return item;
})
this.setState({data : newdata});
}
AllOnDeleteItem (id) {
console.log(id);
let newdata = this.state.data.map(function (item) {
console.log(item);
if (item.id == id) {
item.deleteFlag = true
}
return item
})
this.setState({ data: newdata })
}
...
<AppList
data={this.state.data}
choosevalue={this.state.choosevalue}
ChangeCompleteTop={this.AllChangeComplete.bind(this)}
DeleteItemTop={this.AllOnDeleteItem.bind(this)}
/>
...
AppList.js
我们根据complete与deleteFlag来进行渲染。并在这个组件中,充当一个中间的过度组件,将AppTodos触发的函数传到App.js中去改变状态。
...
class AppList extends React.Component {
SubmitDelete (id) {
this.props.DeleteItemTop(id)
}
ChangeDone (id) {
this.props.ChangeCompleteTop(id);
}
render () {
var value = this.props.choosevalue;
const a = this.props.data.map(({ id, text, complete }, index) => {
if (value == ‘1‘ && deleteFlag !== true) {
return <AppTodos
key={index}
id={id}
text={text}
complete={complete}
ChangeCompleteItem={this.ChangeDone.bind(this)} DeleteItem={this.SubmitDelete.bind(this)}
/>
}
if (value == ‘2‘ && complete === false && deleteFlag !== true) {
return <AppTodos
key={index}
id={id}
text={text}
complete={complete}
ChangeCompleteItem={this.ChangeDone.bind(this)} DeleteItem={this.SubmitDelete.bind(this)}
/>
}
if (value == ‘3‘ && complete === true && deleteFlag !== true) {
return <AppTodos
key={index}
id={id}
text={text}
complete={complete}
ChangeCompleteItem={this.ChangeDone.bind(this)} DeleteItem={this.SubmitDelete.bind(this)}
/>
}
})
return (
<div> { a } </div>
)
}
}
export default AppList;
AppTodos.js
添加两个方法,handleChangeComplete和handleDelete。一个时更改状态,一个是删除。
...
handleChangeComplete () {
let newComplete = this.props;
this.props.ChangeCompleteItem(this.props.id);
}
handleDelete () {
this.props.DeleteItem(this.props.id);
}
render () {
return (
<div className=‘comment‘>
<div className=‘content‘>
<span className=‘author‘
style={styles.title}
onClick={this.handleChangeComplete.bind(this)}>
{this.props.text}
<span className={this.props.complete ? ‘line‘ : ‘‘} />
</span>
<span className=‘author‘
style={styles.title}>
{this.props.complete ? ‘已完成‘ : ‘未完成‘}
</span>
<span className=‘author‘>{this.props.id}</span>
<span className=‘ui blue button‘
style={styles.delete}
onClick={this.handleDelete.bind(this)}>
删除
</span>
</div>
</div>
)
}
}
export default AppTodos;
至此我们完成了所有的功能,如下图所示:

我们这次没有借助任何的状态管理工具,如redux来做这个demo。因为大家做下来也能发现,其实我们这个demo的层级只有2层,所以我们可以通过props来进行父子间的通信。
但是其实我们也发现了,如果当组件的层级越来越多的时候,我们通过这样来进行父子间的通信就不方便了,在这个时候我们就需要用到redux或者mobx等等的状态管理工具。
其实这边的删除和修改list状态我都是在前端模拟处理的,在实际工作中我们都会通过接口去处理,然后根据返回值进行更改state。
希望这篇文章对大家有一点启发,有任何问题可以在简书里私信我哦!
来自一个奔跑在前端路上的前端小白。
标签:footer 工作 ebe 参考 genius 功能 detail lang lan
原文地址:https://www.cnblogs.com/pangguoming/p/12247875.html