todolist for react redux 学习总结

前端之家收集整理的这篇文章主要介绍了todolist for react redux 学习总结前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

最近一直在学习react技术栈,相关的理论和概念基本都了解了,之前也用reactjs写了几个demo,切身体会到了函数式编程和组件化开发的强大之处,但因各种主客观原因,事后没有对相关知识点进行梳理和总结,而且工作中也没用到,导致现在复习的时候生疏了,还需要花大部分时间重新理清需求和逻辑,做了很多重复性的工作,太得不偿失了。为了提高自己的学习效率,避免做一些无用的工作,我也决定以后(无论是工作还是学习)一定要养成定时总结的习惯,而且也要用文字记录下来,这样可以时常复习,理清逻辑,加深印象。另外,关于我个人的学习总结,如有不对的地方,欢迎批评指正,期待共同提高!不喜勿喷,谢谢!

第一章 页面搭建

目录结构

  1. ├── components
  2. | └──app.css//样式文件
  3. ├── node_modules //依赖包
  4. ├── static //静态文件
  5. | └──index.html //入口html文件
  6. | └──bundle.js //编译后的js文件
  7. ├── index.js //主入口js文件
  8. ├── package.json //项目所依赖的npm包
  9. ├── webpack.config.js //webpack配置文件
  10. └── yarn.lock //依赖或者更新包相关版本信息。这样可以解决同一个项目在不同机器上环境不一致的问题。

包管理文件 package.json

  1. {
  2. "name": "todolist","version": "1.0.0","description": "","main": "index.js","scripts": {
  3. "build": "webpack","start": "webpack-dev-server --line --hot","test": "echo \"Error: no test specified\" && exit 1"
  4. },"author": "www.icrazyman.cn","license": "ISC","devDependencies": {
  5. "babel-core": "^6.21.0","babel-loader": "^6.2.10","babel-preset-es2015": "^6.18.0","babel-preset-react": "^6.16.0","css-loader": "^0.28.4","react": "^15.4.1","react-dom": "^15.4.1","style-loader": "^0.18.2","webpack": "^1.14.0","webpack-dev-server": "^1.16.2"
  6. },"dependencies": {
  7. "node": "^6.11.1"
  8. }
  9. }

入口文件index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <Meta charset="UTF-8">
  5. <title>Redux Todos Example</title>
  6. </head>
  7. <body>
  8. <div class="todoapp" id="root"></div>
  9. <script src="bundle.js"></script>
  10. </body>
  11. </html>

index.js

import第三方依赖包和css文件
  1. import React from 'react'
  2. import { render } from 'react-dom'
  3. import './components/app.css'
  4.  
  5. render(
  6. <div className='todo-Box'>
  7. <div className='todo-innerBox'>
  8. <div className='todo-tab'>
  9. <div className='todo-tab_item'>
  10. <a href='javascript:;'>全部任务</a>
  11. </div>
  12. <div className='todo-tab_item'>
  13. <a href='javascript:;'>待办任务</a>
  14. </div>
  15. <div className='todo-tab_item'>
  16. <a href='javascript:;'>已完成任务</a>
  17. </div>
  18. </div>
  19. <ul className='list-group'>
  20. <li className="todo-list_li">
  21. <input type="checkBox" className="pull-left" value="on" />
  22. aaa
  23. </li>
  24. <li className="todo-list_li">
  25. <input type="checkBox" className="pull-left" value="on" />
  26. bbb
  27. </li>
  28. <li className="todo-list_li">
  29. <input type="checkBox" className="pull-left" value="on" />
  30. ccc
  31. </li>
  32. </ul>
  33. <div>
  34. <form>
  35. <input placeholder='你想做点什么' className='todo-input' />
  36. <button type='submit' className='todo-btn'>添加任务</button>
  37. </form>
  38. </div>
  39. </div>
  40. </div>,document.getElementById('root')
  41. )

webpack配置文件webpack.config.js

  1. module.exports = {
  2. devtool: 'eval-source-map',//选择map来增强调试过程
  3. entry: __dirname + '/index.js',//入口文件
  4. output: {
  5. path: __dirname + '/static',//打包生成路径
  6. filename: 'bundle.js'
  7. },module: {
  8. loaders: [{
  9. test: /\.js$/,exclude: /node_modules/,loader: 'babel',query: {
  10. presets: ['es2015','react']
  11. }
  12. },{ test: /\.css$/,loader: 'style-loader!css-loader' }]
  13. },devServer: { //热更新
  14. contentBase: './static',historyApiFallback: true,inline: true,hot: true
  15. }
  16. }

打开终端运行yarnnpm install安装依赖,运行npm run build编译,运行npm start进行查看页面是否正常显示

小结:这个阶段只是把页面实现出来了,还没有实现任何逻辑。其中页面实现的步骤为:
  1. 1. index.html编写html结构和css样式
  2. 2. html结构提取index.js组件中同时转换成jsx语法
  3. 3. css样式提取app.css

第二章 引入redux组件化

目录结构

  1. ├── actions
  2. | └──index.js//管理状态
  3. ├── components
  4. | └──app.css//样式文件
  5. | └──App.js//UI组件入口文件
  6. | └──Link.js//UI组件
  7. | └──Todo.js//UI组件
  8. | └──Top.js//UI组件
  9. ├── containers
  10. | └──VisibleTodoList.js//容器组件
  11. | └──AddTodo.js//容器组件
  12. | └──FilterLink.js//容器组件
  13. ├── reducers
  14. | └──index.js//数据逻辑处理文件
  15. ├── node_modules //依赖包
  16. ├── static //静态文件
  17. | └──index.html //入口html文件
  18. | └──bundle.js //编译后的js文件
  19. ├── index.js //主入口js文件
  20. ├── package.json //项目所依赖的npm包
  21. ├── webpack.config.js //webpack配置文件
  22. └── yarn.lock //依赖或者更新包相关版本信息。这样可以解决同一个项目在不同机器上环境不一致的问题。

安装依赖包

此次新增react-redux和redux依赖包
  1. {
  2. "name": "todolist","author": "","react-redux": "^5.0.1","redux": "^3.6.0","dependencies": {
  3. "node": "^6.11.1"
  4. }
  5. }

index.js

此次利用redux管理整个项目的状态,并且把项目代码抽离成组件
  1. //注意:import后不加{}代表引入的default,加了{}代表引入其中导出的一部分,用到了ES6的解构
  2. //注意:import后不加{}代表引入的default,加了{}代表引入其中导出的一部分,用到了ES6的解构
  3. import React from 'react'
  4. import { render } from 'react-dom'
  5. import { Provider } from 'react-redux'
  6. import { createStore } from 'redux'
  7. //数据逻辑处理保存在reducers里
  8. import todoApp from './reducers'
  9. import App from './components/App'
  10. //在顶层创建store管理整个项目的数据
  11. const store = createStore(todoApp)
  12. console.log('9. root index start')
  13. //connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
  14. // 一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。
  15. // React-Redux 提供Provider组件,可以让容器组件拿到state。
  16. //Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。
  17. render(
  18. <Provider store={store}>
  19. <App />
  20. </Provider>,document.getElementById('root')
  21. )
  22. console.log('9. root index end')

reducers/index.js

把数据逻辑处理的代码抽离成reducers
  1. import { combineReducers } from 'redux'
  2. console.log('data flow:')
  3. console.log('1. reducers start')
  4. /* 传入旧的state和作用的action返回一个新state */
  5. const todo = (state,action) => {
  6. console.log('data flow 4')
  7. switch(action.type) {
  8. case 'ADD_TODO':
  9. return {
  10. id: action.id,text: action.text,completed: false //新增默认为未完成
  11. }
  12. case 'TOGGLE_TODO':
  13. if (state.id !== action.id) {
  14. return state
  15. }
  16. return Object.assign({},state,{completed: !state.completed})
  17. default:
  18. return state
  19. }
  20. }
  21.  
  22. const todos = (state = [],action) => {
  23. console.log('1.1 todos twice')
  24. switch(action.type) {
  25. case 'ADD_TODO':
  26. return [
  27. ...state,todo(undefined,action)
  28. ]
  29. //todo(undefined,action) 新增一条记录时第一个参数state不存在
  30. case 'TOGGLE_TODO':
  31. return state.map(t => todo(t,action));
  32. default:
  33. return state;
  34. }
  35. }
  36.  
  37. const visibilityFilter = (state = 'SHOW_ALL',action) => {
  38. console.log('1.2 visibilityFilter twice')
  39. switch (action.type) {
  40. case 'SET_VISIBILITY':
  41. return action.filter;
  42. default:
  43. return state;
  44. }
  45. }
  46. console.log('1. reducers end')
  47. export default combineReducers({
  48. todos,visibilityFilter
  49. });

actions/index.js

在redux中,actions是触发store中数据更新的唯一来源,所以要写个actions利用dispatch方法来触发store中管理的状态更新
  1. let nextTodoId = 0;
  2. console.log('2. actions start')
  3.  
  4. // 添加
  5. const addTodo = (text) => {
  6. // console.log('text:' + text)
  7. console.log('data flow 2')
  8. let id = nextTodoId ++ ;
  9. return {
  10. type: 'ADD_TODO',id: id,text
  11. }
  12. }
  13.  
  14. // 顶部显示状态
  15. const setVisibility = (filter) => {
  16. // console.log('filter:' + filter)
  17. return {
  18. type: 'SET_VISIBILITY',filter
  19. }
  20. }
  21.  
  22. // 触发
  23. const toggleTodo = (id) => {
  24. // console.log('id:' + id)
  25. return {
  26. type: 'TOGGLE_TODO',id
  27. }
  28. }
  29. export {addTodo,setVisibility,toggleTodo}
  30. console.log('2. actions end')

components/App.js

子组件的入口文件,负责对抽离出的子组件进行组合然后导出
  1. import React from 'react'
  2. import Top from './Top'
  3. import VisibleTodoList from '../containers/VisibleTodoList'
  4. import AddTodo from '../containers/AddTodo'
  5. import './app.css'
  6. console.log('8. App.js start')
  7. const App = () => (
  8. <div className='todo-Box'>
  9. <div className='todo-innerBox'>
  10. <Top />
  11. <VisibleTodoList />
  12. <AddTodo />
  13. </div>
  14. </div>
  15. )
  16. console.log('8. App.js end')
  17. export default App;

components/Top.js

TodoList顶部组件
  1. import React from 'react'
  2. import { connect } from 'react-redux'
  3. import { setVisibility } from '../actions'
  4. import FilterLink from '../containers/FilterLink'
  5. console.log('4. Top.js start')
  6.  
  7. const Top = () => (
  8. <div className="todo-tab">
  9. <FilterLink filter="SHOW_ALL">
  10. 全部任务
  11. </FilterLink>
  12. <FilterLink filter="SHOW_ACTIVE">
  13. 待办任务
  14. </FilterLink>
  15. <FilterLink filter="SHOW_COMPLETED">
  16. 已完成任务
  17. </FilterLink>
  18. </div>
  19. );
  20. console.log('4. Top.js end')
  21. export default Top;

containers/FilterLink.js

过滤组件
  1. import React from 'react'
  2. import { connect } from "react-redux"
  3. import { setVisibility } from "../actions"
  4. import Link from "../components/Link"
  5. //第二个参数表示组件自身的props
  6. //mapStateToProps()将state节点注入到与view相关的组件
  7. const mapStateToProps = (state,ownProps) => {
  8. // console.log({state,ownProps})
  9. return {
  10. active: ownProps.filter === state.visibilityFilter
  11. }
  12. }
  13. //mapDispatchToProps()将需要绑定的响应事件注入到组件上
  14. const mapDispatchToProps = (dispatch,ownProps) => {
  15. return {
  16. onClick: () => {
  17. dispatch(setVisibility(ownProps.filter))
  18. }
  19. }
  20. }
  21. //connent()函数生成容器组件
  22. const FilterLink = connect(
  23. mapStateToProps,mapDispatchToProps
  24. )(Link)
  25.  
  26. export default FilterLink;

components/Link.js

展示组件,显示顶部状态按钮
  1. import React from 'react';
  2. console.log('3. Link.js start')
  3. const Link = ({ active,children,onClick }) => {
  4. return (
  5. <div className="todo-tab_item">
  6. <a href = "#" style={{ color: active? '#f01414' : '#4d555d' }}
  7. onClick = {
  8. e => {
  9. e.preventDefault()
  10. onClick()
  11. }
  12. }>
  13. {children}
  14. </a>
  15. </div>
  16. )
  17. }
  18. console.log('3. Link.js end')
  19. export default Link;

components/Todo.js

每一个Todo子组件
  1. import React from 'react'
  2. console.log('5. Todo.js start')
  3. //UI 组件有以下几个特征。
  4. // 只负责 UI 的呈现,不带有任何业务逻辑
  5. // 没有状态(即不使用this.state这个变量)
  6. // 所有数据都由参数(this.props)提供
  7. // 不使用任何 Redux 的 API
  8. // UI 组件又称为"纯组件",不含状态,纯粹由参数决定它的值
  9. const Todo = ({ onClick,completed,text }) => (
  10. <li className='todo-list_li' style={{textDecoration:completed ? "line-through" : "none"}}>
  11. <input type='checkBox' className='pull-left' value='on' onClick={onClick} defaultChecked={completed} />
  12. {text}
  13. </li>
  14. )
  15. console.log('5. Todo.js end')
  16. export default Todo;

containers/VisibleTodoList.js

TodoList列表组件
  1. import React from 'react'
  2. import Todo from '../components/Todo'
  3. import { connect } from 'react-redux'
  4. import { toggleTodo } from '../actions'
  5. console.log('6. VisibleTodoList start')
  6. //负责管理数据和业务逻辑,不负责 UI 的呈现
  7. // 带有内部状态
  8. // 使用 Redux 的 API
  9. const getVisibleTodos = (todos,filter) => {
  10. console.log('9.2 getVisibleTodos')
  11. switch (filter) {
  12. case 'SHOW_ALL':
  13. return todos;
  14. case 'SHOW_COMPLETED':
  15. return todos.filter(t => t.completed === true)//已完成
  16. case 'SHOW_ACTIVE':
  17. return todos.filter(t => t.completed === false)//未完成
  18. default:
  19. throw new Error('Unknown filter: ' + filter)
  20. }
  21. }
  22. //合并redux的状态到react的props中
  23. //将state映射到 UI 组件的参数(props)
  24. //mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
  25. //mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。
  26. //使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
  27. const mapStateToProps = (state,ownProps) => {
  28. console.log('9.1 mapStateToProps')
  29. // console.log(ownProps)
  30. // console.log(state)
  31. return {
  32. todos: getVisibleTodos(state.todos,state.visibilityFilter)
  33. }
  34. }
  35. //将用户对 UI 组件的操作映射成 Action
  36. //建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象
  37. //如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。
  38. //mapDispatchToProps作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
  39. //如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
  40. const mapDispatchToProps = (dispatch) => {
  41. return {
  42. onTodoClick: (id) => {
  43. // console.log(id)
  44. dispatch(toggleTodo(id))
  45. }
  46. }
  47. }
  48.  
  49. const TodoList = ({ todos,onTodoClick }) => {
  50. console.log('9.3 TodoList')
  51. // console.log(todos);//todos数组
  52. return (
  53. <ul className='list-group'>
  54. {todos.map(todo =>
  55. <Todo
  56. key={todo.id}
  57. {...todo}
  58. onClick={() => onTodoClick(todo.id)}
  59. />
  60. )}
  61. </ul>
  62. )
  63. }
  64. //React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。
  65. //connect方法接受两个参数:mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
  66. //connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
  67. const VisibleTodoList = connect(
  68. mapStateToProps,mapDispatchToProps
  69. )(TodoList)
  70. console.log('6. VisibleTodoList end')
  71. export default VisibleTodoList;

containers/AddTodo.js

添加Todo子组件
  1. import React from 'react'
  2. import { connect } from 'react-redux'
  3. import { addTodo } from '../actions'
  4. console.log('7. AddTodo.js start')
  5. let AddTodo = ({ dispatch }) => {
  6. let input;
  7. return (
  8. <div>
  9. <form onSubmit={ e => {
  10. console.log('data flow 1')
  11. e.preventDefault();
  12. if (!input.value.trim()) { return }
  13. dispatch(addTodo(input.value))
  14. input.value = ''
  15. } }>
  16. <input placeholder='你想做点什么' ref={r => input = r
  17. } className='todo-input' autoFocus={true}/>
  18. <button type='submit' className='todo-btn'>
  19. 添加任务
  20. </button>
  21. </form>
  22. </div>
  23. )
  24. }
  25.  
  26. AddTodo = connect()(AddTodo)//把addTodo用redux的connect方法重新包装一下,使其可使用state中的数据
  27. console.log('7. AddTodo.js end')
  28. export default AddTodo;

打开终端运行yarnnpm install安装依赖,运行npm run build编译,运行npm start进行查看页面功能是否正常

小结:这个阶段把页面功能实现出来了,初步把整个项目抽离成组件,也利用redux把代码业务逻辑处理、状态管理和状态分发管理分离出来了

总结:本次todolist的项目因时间关系中间断了两次,也算是“因祸得福”吧!我也把这个项目完整地复习了一遍。这个项目主要把redux的运用以及数据流了解了下,算是大概清楚怎么玩了,为什么说大概呢?一个是因为目前工作中没有使用,无法运用到实际项目中,一个就是最外层的index.js,不明白为什么console.log是最后打印出来的,也就意味这是最后加载这个文件的,这个文件我的理解不应该是整个项目js的主入口文件吗?为什么反而最后加载呢?不明白,先记下来吧,等以后用到redux了再有意识地研究一下。现在把自己做这个项目的详细步骤、个人理解以及查阅相关的资料整理出来了,不一定十分准确,如果有哪位大神有不同的意见欢迎批评指正!最新了解了react,很喜欢它的组件化开发,奈何公司用的却是JQ,所以在react里我其实还只是个新手,不喜勿喷啊!

猜你在找的React相关文章