资讯专栏INFORMATION COLUMN

todolist for react redux 学习总结

anquan / 1840人阅读

摘要:为了提高自己的学习效率,避免做一些无用的工作,我也决定以后无论是工作还是学习一定要养成定时总结的习惯,而且也要用文字记录下来,这样可以时常复习,理清逻辑,加深印象。一种解决方法是将对象作为参数,传入容器组件。

前言
最近一直在学习react技术栈,相关的理论和概念基本都了解了,之前也用reactjs写了几个demo,切身体会到了函数式编程和组件化开发的强大之处,但因各种主客观原因,事后没有对相关知识点进行梳理和总结,而且工作中也没用到,导致现在复习的时候生疏了,还需要花大部分时间重新理清需求和逻辑,做了很多重复性的工作,太得不偿失了。为了提高自己的学习效率,避免做一些无用的工作,我也决定以后(无论是工作还是学习)一定要养成定时总结的习惯,而且也要用文字记录下来,这样可以时常复习,理清逻辑,加深印象。另外,关于我个人的学习总结,如有不对的地方,欢迎批评指正,期待共同提高!不喜勿喷,谢谢!
第一章 页面搭建 目录结构
├── components
|   └──app.css//样式文件
├── node_modules //依赖包
├── static //静态文件
|   └──index.html //入口html文件
|   └──bundle.js //编译后的js文件
├── index.js //主入口js文件
├── package.json //项目所依赖的npm包
├── webpack.config.js //webpack配置文件
└── yarn.lock //依赖或者更新包相关版本信息。这样可以解决同一个项目在不同机器上环境不一致的问题。
包管理文件 package.json
{
  "name": "todolist",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack",
    "start": "webpack-dev-server --line --hot",
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "www.icrazyman.cn",
  "license": "ISC",
  "devDependencies": {
    "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"
  },
  "dependencies": {
    "node": "^6.11.1"
  }
}
入口文件index.html



    
    Redux Todos Example


    
index.js
import第三方依赖包和css文件
import React from "react"
import { render } from "react-dom"
import "./components/app.css"

render(
  
  • aaa
  • bbb
  • ccc
, document.getElementById("root") )
webpack配置文件webpack.config.js
module.exports = {
  devtool: "eval-source-map", //选择map来增强调试过程
  entry: __dirname + "/index.js", //入口文件
  output: {
    path: __dirname + "/static",//打包生成路径
    filename: "bundle.js" 
  },
  module: {
    loaders: [{
      test: /.js$/,
      exclude: /node_modules/,
      loader: "babel",
      query: {
        presets: ["es2015", "react"]
      }
    }, { test: /.css$/, loader: "style-loader!css-loader" }]
  },
  devServer: { //热更新
    contentBase: "./static",
    historyApiFallback: true,
    inline: true,
    hot: true
  }
}
打开终端运行yarnnpm install安装依赖,运行npm run build编译,运行npm start进行查看页面是否正常显示
小结:这个阶段只是把页面实现出来了,还没有实现任何逻辑。其中页面实现的步骤为:
1. 在index.html编写html结构和css样式
2. 把html结构提取到index.js组件中同时转换成jsx语法
3. 把css样式提取到app.css中

第二章 引入redux组件化 目录结构
├── actions
|   └──index.js//管理状态
├── components
|   └──app.css//样式文件
|   └──App.js//UI组件入口文件
|   └──Link.js//UI组件
|   └──Todo.js//UI组件
|   └──Top.js//UI组件
├── containers
|   └──VisibleTodoList.js//容器组件
|   └──AddTodo.js//容器组件
|   └──FilterLink.js//容器组件
├── reducers
|   └──index.js//数据逻辑处理文件
├── node_modules //依赖包
├── static //静态文件
|   └──index.html //入口html文件
|   └──bundle.js //编译后的js文件
├── index.js //主入口js文件
├── package.json //项目所依赖的npm包
├── webpack.config.js //webpack配置文件
└── yarn.lock //依赖或者更新包相关版本信息。这样可以解决同一个项目在不同机器上环境不一致的问题。
安装依赖包
此次新增react-redux和redux依赖包
{
  "name": "todolist",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack",
    "start": "webpack-dev-server --line --hot",
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "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",
    "react-redux": "^5.0.1",
    "redux": "^3.6.0",
    "style-loader": "^0.18.2",
    "webpack": "^1.14.0",
    "webpack-dev-server": "^1.16.2"
  },
  "dependencies": {
    "node": "^6.11.1"
  }
}
index.js
此次利用redux管理整个项目的状态,并且把项目代码抽离成组件
//注意:import后不加{}代表引入的default,加了{}代表引入其中导出的一部分,用到了ES6的解构
//注意:import后不加{}代表引入的default,加了{}代表引入其中导出的一部分,用到了ES6的解构
import React from "react"
import { render } from "react-dom"
import { Provider } from "react-redux"
import { createStore } from "redux"
//数据逻辑处理保存在reducers里
import todoApp from "./reducers"
import App from "./components/App"
//在顶层创建store管理整个项目的数据
const store = createStore(todoApp)
console.log("9. root index start")
//connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
// 一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。
// React-Redux 提供Provider组件,可以让容器组件拿到state。
//Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。
render(
  
    
  
  ,document.getElementById("root")
)
console.log("9. root index end")
reducers/index.js
把数据逻辑处理的代码抽离成reducers
import { combineReducers } from "redux"
console.log("data flow:")
console.log("1. reducers start")
/* 传入旧的state和作用的action返回一个新state */
const todo = (state, action) => {
    console.log("data flow 4")
    switch(action.type) {
        case "ADD_TODO":
            return {
                id: action.id,
                text: action.text,
                completed: false    //新增默认为未完成
            }
        case "TOGGLE_TODO":
            if (state.id !== action.id) {
                return state
            }
            return Object.assign({}, state, {completed: !state.completed})
        default:
            return state
    }
}

const todos = (state = [], action) => {
    console.log("1.1 todos twice")
    switch(action.type) {
        case "ADD_TODO":
            return [
                ...state,
                todo(undefined, action)
            ]
            //todo(undefined, action) 新增一条记录时第一个参数state不存在
        case "TOGGLE_TODO":
            return state.map(t => todo(t, action));
        default:
            return state;
    }
}

const visibilityFilter = (state = "SHOW_ALL", action) => {
    console.log("1.2 visibilityFilter twice")
    switch (action.type) {
        case "SET_VISIBILITY":
            return action.filter;
        default:
            return state;
    }
}
console.log("1. reducers end")
export default combineReducers({
    todos,
    visibilityFilter
});
actions/index.js
在redux中,actions是触发store中数据更新的唯一来源,所以要写个actions利用dispatch方法来触发store中管理的状态更新
let nextTodoId = 0;
console.log("2. actions start")

// 添加
const addTodo = (text) => {
    // console.log("text:" + text)
    console.log("data flow 2")
    let id = nextTodoId ++ ;
    return {
        type: "ADD_TODO",
        id: id,
        text
    }
}

// 顶部显示状态
const setVisibility = (filter) => {
    // console.log("filter:" + filter)
    return {
        type: "SET_VISIBILITY",
        filter
    }
}

// 触发
const toggleTodo = (id) => {
    // console.log("id:" + id)
    return {
        type: "TOGGLE_TODO",
        id
    }
}
export {addTodo, setVisibility, toggleTodo}
console.log("2. actions end")
components/App.js
子组件的入口文件,负责对抽离出的子组件进行组合然后导出
import React from "react"
import Top from "./Top"
import VisibleTodoList from "../containers/VisibleTodoList"
import AddTodo from "../containers/AddTodo"
import "./app.css"
console.log("8. App.js start")
const App = () => (
  
) console.log("8. App.js end") export default App;
components/Top.js
TodoList顶部组件
import React from "react"
import { connect } from "react-redux"
import { setVisibility } from "../actions"
import FilterLink from "../containers/FilterLink"
console.log("4. Top.js start")

const Top = () => (
    
全部任务 待办任务 已完成任务
); console.log("4. Top.js end") export default Top;
containers/FilterLink.js
过滤组件
import React from "react"
import { connect } from "react-redux"
import { setVisibility } from "../actions"
import Link from "../components/Link"
//第二个参数表示组件自身的props
//mapStateToProps()将state节点注入到与view相关的组件
const mapStateToProps = (state, ownProps) => {
    // console.log({state,ownProps})
    return {
        active: ownProps.filter === state.visibilityFilter
    }
}
//mapDispatchToProps()将需要绑定的响应事件注入到组件上
const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onClick: () => {
            dispatch(setVisibility(ownProps.filter))
        }
    }
}
//connent()函数生成容器组件
const FilterLink = connect(
    mapStateToProps,
    mapDispatchToProps
)(Link)

export default FilterLink;
components/Link.js
展示组件,显示顶部状态按钮
import React from "react";
console.log("3. Link.js start")
const Link = ({ active, children, onClick }) => {
    return (
        
    )
}
console.log("3. Link.js end")
export default Link;
components/Todo.js
每一个Todo子组件
import React from "react"
console.log("5. Todo.js start")
//UI 组件有以下几个特征。
// 只负责 UI 的呈现,不带有任何业务逻辑
// 没有状态(即不使用this.state这个变量)
// 所有数据都由参数(this.props)提供
// 不使用任何 Redux 的 API
// UI 组件又称为"纯组件",不含状态,纯粹由参数决定它的值
const Todo = ({ onClick, completed, text }) => (
  
  • {text}
  • ) console.log("5. Todo.js end") export default Todo;
    containers/VisibleTodoList.js
    TodoList列表组件
    import React from "react"
    import Todo from "../components/Todo"
    import { connect } from "react-redux"
    import { toggleTodo } from "../actions"
    console.log("6. VisibleTodoList start")
    //负责管理数据和业务逻辑,不负责 UI 的呈现
    // 带有内部状态
    // 使用 Redux 的 API
    const getVisibleTodos = (todos, filter) => {
        console.log("9.2 getVisibleTodos")
        switch (filter) {
            case "SHOW_ALL":
                return todos;
            case "SHOW_COMPLETED":
                return todos.filter(t => t.completed === true)//已完成
            case "SHOW_ACTIVE":
                return todos.filter(t => t.completed === false)//未完成
            default:
                throw new Error("Unknown filter: " + filter)
        }
    }
    //合并redux的状态到react的props中
    //将state映射到 UI 组件的参数(props)
    //mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
    //mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。
    //使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
    const mapStateToProps = (state, ownProps) => {
        console.log("9.1 mapStateToProps")
        // console.log(ownProps)
        // console.log(state)
        return {
            todos: getVisibleTodos(state.todos, state.visibilityFilter)
        }
    }
    //将用户对 UI 组件的操作映射成 Action
    //建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象
    //如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。
    //mapDispatchToProps作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
    //如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
    const mapDispatchToProps = (dispatch) => {
        return {
            onTodoClick: (id) => {
                // console.log(id)
                dispatch(toggleTodo(id))
            }
        }
    }
    
    const TodoList = ({ todos, onTodoClick }) => {
        console.log("9.3 TodoList")
        // console.log(todos);//todos数组
        return (
            
      {todos.map(todo => onTodoClick(todo.id)} /> )}
    ) } //React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。 //connect方法接受两个参数:mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。 //connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。 const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) console.log("6. VisibleTodoList end") export default VisibleTodoList;
    containers/AddTodo.js
    添加Todo子组件
    import React from "react"
    import { connect } from "react-redux"
    import { addTodo } from "../actions"
    console.log("7. AddTodo.js start")
    let AddTodo = ({ dispatch }) => {
        let input;
        return (
            
    { console.log("data flow 1") e.preventDefault(); if (!input.value.trim()) { return } dispatch(addTodo(input.value)) input.value = "" } }> input = r } className="todo-input" autoFocus={true}/>
    ) } AddTodo = connect()(AddTodo)//把addTodo用redux的connect方法重新包装一下,使其可使用state中的数据 console.log("7. AddTodo.js end") export default AddTodo;
    打开终端运行yarnnpm install安装依赖,运行npm run build编译,运行npm start进行查看页面和功能是否正常
    小结:这个阶段把页面和功能实现出来了,初步把整个项目抽离成组件,也利用redux把代码业务逻辑处理、状态管理和状态分发管理分离出来了

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

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/94814.html

    相关文章

    • Redux 基础 - react 全家桶学习笔记(一)

      摘要:二基础就是一个普通的。其他属性用来传递此次操作所需传递的数据,对此不作限制,但是在设计时可以参照标准。对于异步操作则将其放到了这个步骤为添加一个变化监听器,每当的时候就会执行,你可以在回调函数中使用来得到当前的。 注:这篇是16年10月的文章,搬运自本人 blog...https://github.com/BuptStEve/... 零、环境搭建 参考资料 英文原版文档 中文文档 墙...

      aaron 评论0 收藏0
    • dva框架使用详解及Demo教程

      摘要:框架的使用详解及教程在前段时间,我们也学习讲解过框架的基本使用,但是有很多同学在交流群里给我的反馈信息说,框架理解上有难度,看了之后还是一脸懵逼不知道如何下手,很多同学就转向选择使用框架。 dva框架的使用详解及Demo教程 在前段时间,我们也学习讲解过Redux框架的基本使用,但是有很多同学在交流群里给我的反馈信息说,redux框架理解上有难度,看了之后还是一脸懵逼不知道如何下手,很...

      bergwhite 评论0 收藏0
    • 实例讲解react+react-router+redux

      摘要:而函数式编程就不一样了,这是模仿我们人类的思维方式发明出来的。数据流在中,数据的流动是单向的,即从父节点传递到子节点。数据流严格的单向数据流是架构的设计核心。 前言 总括: 本文采用react+redux+react-router+less+es6+webpack,以实现一个简易备忘录(todolist)为例尽可能全面的讲述使用react全家桶实现一个完整应用的过程。 代码地址:Re...

      RiverLi 评论0 收藏0
    • TypeScript 3.0 + React + Redux 最佳实践

      摘要:首先声明这篇文章是想说明一下最新版本的的新特性带来的极大的开发体验提升而不是如何利用开发应用这个特性就是对的支持在的中有说明具体可以参考这里在版本之前我们在开发应用尤其是在配合一类库的时候经常用到诸如之类的封装而这些函数其实都可以用装饰器的 首先声明, 这篇文章是想说明一下最新版本的 TypeScript(3.0) 的新特性带来的极大的 React 开发体验提升. 而不是如何利用 Ty...

      CloudwiseAPM 评论0 收藏0
    • 你要的 React 面试知识点,都在这了

      摘要:是流行的框架之一,在年及以后将会更加流行。于年首次发布,多年来广受欢迎。下面是另一个名为的高阶函数示例,该函数接受另外两个函数,分别是和。将所有较小的函数组合成更大的函数,最终,得到一个应用程序,这称为组合。 React是流行的javascript框架之一,在2019年及以后将会更加流行。React于2013年首次发布,多年来广受欢迎。它是一个声明性的、基于组件的、用于构建用户界面的高...

      klinson 评论0 收藏0

    发表评论

    0条评论

    anquan

    |高级讲师

    TA的文章

    阅读更多
    最新活动
    阅读需要支付1元查看
    <