前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >react新手demo——TodoList

react新手demo——TodoList

作者头像
庞小明
发布2020-02-18 15:39:49
1K0
发布2020-02-18 15:39:49
举报
文章被收录于专栏:pangguoming

react-todolist.gif

一: 写在文章开头

今天我们就使用 react 来实现一个简易版的 todolist ,我们可以使用这个 demo 进行 list 的增删改差,实际效果如上图所示。大家可以clone下来查看:react-todolist

这篇文章我们就不使用 redux,因为这个 demo 本身比较简单,不需要通过 redux 来管理我们的状态。

redux中也有非常有名的一句话叫做:

"如果你不知道是否需要 Redux,那就是不需要它。"

我们废话不多说,直接进入正题。


二:项目的目录结构
代码语言:javascript
复制
   .
   ├── 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我们可以自己创建。

代码语言:javascript
复制
$ mkdir todolist
$ cd todolist

2,建立package.json文件

代码语言:javascript
复制
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打包,通过使用不同的loaderwebpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换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里面的组件
    1. App.js 这个组件我们可以认为是一个容器组件,我们会把AppFormAppListAppFooter放在这个组件中。 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;
    2. 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;
    3. 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;
    4. 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;
    5. 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,可看到:

react-demo_1.png


5,实现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; 你可以看到如下效果:

react-add2.gif


6,完成筛选功能
  • 首先里一下流程 我们给下面的三个按钮设置了不同的value1代表全部、2代表未完成、3代表已完成,然后我们根据相应的value,展示相应的list,给三个按钮分别加上handleAllhandleActivehandleComplete三个方法,在onClick时触发。
  • App.js 添加一个加上choosevaluestate,默认为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; ​

你可以看到如下效果:

react-add3.gif


7,修改list状态和删除list的功能
  • 首先里一下流程 修改list的状态,其实就是我们找到相应idlist,然后将其的complete改为true就可以了。 删除list时,就是我们找到相应idlist,我们给他添加一个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 我们根据completedeleteFlag来进行渲染。并在这个组件中,充当一个中间的过度组件,将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 添加两个方法,handleChangeCompletehandleDelete。一个时更改状态,一个是删除。 ... 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;

至此我们完成了所有的功能,如下图所示:

react-add4.gif


8,结语

我们这次没有借助任何的状态管理工具,如redux来做这个demo。因为大家做下来也能发现,其实我们这个demo的层级只有2层,所以我们可以通过props来进行父子间的通信。

但是其实我们也发现了,如果当组件的层级越来越多的时候,我们通过这样来进行父子间的通信就不方便了,在这个时候我们就需要用到redux或者mobx等等的状态管理工具。

其实这边的删除和修改list状态我都是在前端模拟处理的,在实际工作中我们都会通过接口去处理,然后根据返回值进行更改state

希望这篇文章对大家有一点启发,有任何问题可以在简书里私信我哦! 来自一个奔跑在前端路上的前端小白。


9,参考资料

作者:darrell 链接:https://www.jianshu.com/p/211ecf8ed34e 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-02-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一: 写在文章开头
  • 二:项目的目录结构
  • 三:前期准备,安装依赖
  • 四:组件的编写,是我们的页面能够显示出来
  • 5,实现list的添加操作
  • 6,完成筛选功能
  • 7,修改list状态和删除list的功能
  • 8,结语
  • 9,参考资料
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档