上一节讲完了 redux 中的概念,但是仍然没有和 react 联系起来,这一节将利用 redux 在 react 中实现完整的 todolist:

  • 在 react 使用 redux

  • 通过 Provider 连接 react 和 redux store

  • 创建 action creators

  • 创建 reducer

  • 创建 Container Component

  • 床架 Dummy Component

3.2.1 在 react 使用 redux

redux 可以和很多第三方的框架结合起来使用,为了在 react 中使用 redux,可以通过 react-redux

安装 react-redux

$ npm install --save react-redux

3.2.2 通过 Provider 连接 react 和 redux store

react-redux 提供了一个叫 Provider 的组件,将 react 和 react-redux 结合的方式是用 Provider 嵌套应用的 App 组件,并将 redux store 作为属性传递到 Provider 组件之中。

index.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App' /** store 数据结构 sample
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
*/
let store = createStore(todoApp) render(
<Provider store={store}><App /></Provider>,
document.getElementById('root')
)

这里使用到了 React 的 Context ,App 下面的所有组件可以利用 context 获取传入到 Provider 中的 store

3.2.3 创建 action creators

actions/index.js

let nextTodoId = 0
export const addTodo = (text) => {
return {
type: 'ADD_TODO',
id: nextTodoId++,
text
}
} export const setVisibilityFilter = (filter) => {
return {
type: 'SET_VISIBILITY_FILTER',
filter
}
} export const toggleTodo = (id) => {
return {
type: 'TOGGLE_TODO',
id
}
}

3.2.4 创建 reducer

首先创建根 reducer ,通过 redux.combineReducers 方法将其他 reducer 结合起来,每个数据 key 都需要实现一个对应的 reducer

reducer/index.js

import { combineReducers } from 'redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'const todoApp = combineReducers({
todos,
visibilityFilter
}) export default todoApp

接着是 todos reducer , 需要注意的地方是其中使用 Object.assign 方法保证每次都是返回新的对象

reducer/todos.js

const todo = (state, action) => {
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) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
todo(undefined, action)
]
case 'TOGGLE_TODO':
return state.map(t =>
todo(t, action)
)
default:
return state
}
} export default todos

最后是 visibilityFilter

reducers/visibibityFilter.js

const visibilityFilter = (state = 'SHOW_ALL', action) => {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
} export default visibilityFilter

3.2.5 Container Component

在介绍 flux 的时候介绍过组件分两个类型,smart component 和 dummy component,在 redux 中 Container Component 就是 smart component

作用

在 react 应用中,store 的数据只有 container component 能知晓,container component 会将知晓的数据传递给 dummy components ,除此之外 action 的触发方法也会由它传递给 dummy components

connect 方法

react-redux 提供了一个叫 connect 的方法,可以将一个组件变为 container component

const ContainerComponent = connect(
/**
* 方法将 store 作为参数,返回有个 {key: Value} 对象,key 作为属性传递给 DummyComponent
* @type {[type]}
*/
mapStateToProps: Function,
/**
* 方法传递 store.dispatch 作为参数,返回一个{key: Function} 对象,key 作为属性传递给 DummyComponent
* @type {[type]}
*/
mapDispatchToProps: Function
)(DummyComponent)

其本质就是获取react context 中的 store,并将 store 中的数据作为属性传递到原来的组件中

创建 todolist 的 container component

todolist 会分为三个 container,有个负责 todolist,一个负责 filter,一个为添加 todo,首先是 todolist。

containers/VisibleTodoList.js

import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
} const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
} const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
} const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList) export default VisibleTodoList

containers/FilterLink.js

import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'const mapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
}
} const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(setVisibilityFilter(ownProps.filter))
}
}
} const FilterLink = connect(
mapStateToProps,
mapDispatchToProps
)(Link) export default FilterLink

containers/AddTodo.js

import React from 'react'import { connect } from 'react-redux'import { addTodo } from '../actions'let AddTodo = ({ dispatch }) => {
let input return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}>
<input ref={node => {
input = node
}} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo) export default AddTodo

3.2.6 负者展现的 Dummy Components

components/App.js: App.js 连接所有的 Container Components

import React from 'react'import Footer from './Footer'import AddTodo from '../containers/AddTodo'import VisibleTodoList from '../containers/VisibleTodoList'const App = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
) export default App

components/TodoList.js: 展现 todos 列表

import React, { PropTypes } from 'react'import Todo from './Todo'const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
) TodoList.propTypes = {
todos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired).isRequired,
onTodoClick: PropTypes.func.isRequired
} export default TodoList

components/Todo.js

import React, { PropTypes } from 'react'const Todo = ({ onClick, completed, text }) => (
<li
onClick={onClick}
style={{
textDecoration: completed ? 'line-through' : 'none'
}}
>
{text}
</li>
) Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
} export default Todo

components/Link.js

import React, { PropTypes } from 'react'const Link = ({ active, children, onClick }) => {
if (active) {
return <span>{children}</span>
} return (
<a href="#"
onClick={e => {
e.preventDefault()
onClick()
}}
>
{children}
</a>
)
} Link.propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
} export default Link

components/Footer.js

(

Show:
{" "}

All

{", "}

Active

{", "}

Completed

)

export default Footer" title="" data-original-title="复制" style="box-sizing: border-box; cursor: pointer; display: block; float: left; height: 16px; width: 16px; margin: 4px 5px; opacity: 0.5; background-image: url("dcbb9ccc-f2d9-4947-8220-a2eabfd1c426_files/codeTools_3.svg"); background-size: auto 16px; background-position: -16px 0px !important;">

import React from 'react'import FilterLink from '../containers/FilterLink'const Footer = () => (
<p>
Show:
{" "}
<FilterLink filter="SHOW_ALL">
All
</FilterLink>
{", "}
<FilterLink filter="SHOW_ACTIVE">
Active
</FilterLink>
{", "}
<FilterLink filter="SHOW_COMPLETED">
Completed
</FilterLink>
</p>
) export default Footer

3.2 Redux TodoApp的更多相关文章

  1. [Redux] Extracting Presentational Components -- TodoApp

    Finally, I just noticed that the to-do app component doesn't actually have to be a class. I can turn ...

  2. 通过一个demo了解Redux

    TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象:使用数据流能帮我们明确了行为对应的响应,这和react的状态可预测的思想是不谋而合的. 常见的数据流框架 ...

  3. redux学习

    redux学习: 1.应用只有一个store,用于保存整个应用的所有的状态数据信息,即state,一个state对应一个页面的所需信息 注意:他只负责保存state,接收action, 从store. ...

  4. react+redux教程(八)连接数据库的redux程序

    前面所有的教程都是解读官方的示例代码,是时候我们自己写个连接数据库的redux程序了! 例子 这个例子代码,是我自己写的程序,一个非常简单的todo,但是包含了redux插件的用法,中间件的用法,连接 ...

  5. 实例讲解react+react-router+redux

    前言 总括: 本文采用react+redux+react-router+less+es6+webpack,以实现一个简易备忘录(todolist)为例尽可能全面的讲述使用react全家桶实现一个完整应 ...

  6. redux+flux(一:入门篇)

    React是facebook推出的js框架,React 本身只涉及UI层,如果搭建大型应用,必须搭配一个前端框架.也就是说,你至少要学两样东西,才能基本满足需要:React + 前端框架. Faceb ...

  7. [Redux] Fetching Data on Route Change

    We will learn how to fire up an async request when the route changes. A mock server data: /** /api/i ...

  8. [Redux] Wrapping dispatch() to Log Actions

    We will learn how centralized updates in Redux let us log every state change to the console along wi ...

  9. [Redux] Colocating Selectors with Reducers

    We will learn how to encapsulate the knowledge about the state shape in the reducer files, so that t ...

随机推荐

  1. thinkphp 连接多个数据库

    config配置文件 //数据库配置信息 'DB_CONFIG' => array( 'DB_TYPE' => 'mysql', // 数据库类型 'DB_HOST' => 'loc ...

  2. Git命令操作

    安装配置 将其bin目录添加到path ssh -keygen -t rsa -C 自己的邮箱(获取ssh远程连接秘钥) 使用: 进入项目目录 右击进入git bash 执行git init gith ...

  3. Django 模型中的CRUD

    一.通过 ORM 向 DB 中增加数据 1.Entry.objects.create(属性=值,属性=值) Entry:具体要操作的Model类 ex: Author.objects.create(n ...

  4. github中fork分支和pullrequest的最佳实践

    github中fork分支和pullrequest的最佳实践 */--> code {color: #FF0000} pre.src {background-color: #002b36; co ...

  5. 关于软件IntelliJ IDEA的使用技巧(三)

    二,IntelliJ IDEA的工具栏介绍 2,IntelliJ IDEA菜单栏 (9)Tools工具 ✌1.Tasks & Contexts: ✌2.Generate JavaDoc: ✌3 ...

  6. React-Native 使用中的坑

    android 1.键盘会在 ScrollView 之上----不知道是不是未给ScrollView 设置高度的问题 2.navigation的标题在android居中显示办法 navigationO ...

  7. Spring学习笔记(5)——IoC再度学习

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  8. 1、eureka注册中心单机

    Spring Cloud 2.x系列之 eureka注册中心单机 一.简介 Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块.而Spring Clo ...

  9. xargs使用之空格处理

    xargs指定分隔符为'\n' (默认用空格分隔) locate xxx | xargs -d '\n' ls -l xargs使用 -0 参数会以字符串的'\0'结尾为分隔符,可以在文本传给xarg ...

  10. 【Luogu】【关卡2-3】排序(2017年10月) 【AK】

    任务说明:将杂乱无章的数据变得有规律.有各种各样的排序算法,看情况使用. 这里有空还是把各种排序算法总结下吧.qsort需要会写.. P1177 [模板]快速排序 这个题目懒得写了,直接sort了.. ...