惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
Martin Fowler
Martin Fowler
Vercel News
Vercel News
U
Unit 42
Engineering at Meta
Engineering at Meta
aimingoo的专栏
aimingoo的专栏
MyScale Blog
MyScale Blog
Y
Y Combinator Blog
阮一峰的网络日志
阮一峰的网络日志
爱范儿
爱范儿
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
B
Blog RSS Feed
N
Netflix TechBlog - Medium
GbyAI
GbyAI
F
Fortinet All Blogs
MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
C
Check Point Blog
M
MIT News - Artificial intelligence
D
Docker
IT之家
IT之家
Stack Overflow Blog
Stack Overflow Blog

Giscafer's blog

博客停更说明 使用 ViewContainerRef 探索Angular DOM操作 GIS520论坛关闭停止运营 ionic3之组件封装篇 ionic3之图片选择插件com.synconset.imagepicker ionic3开发遇到的一些问题及解决方法 ionic3之自定义tabs菜单图标 ionic3 之Android的actionsheet渲染和ios一致 Hexo博客畅言评论插件试用 从GISer到互联网前端工程师,JUST DO IT angular实现IM聊天图片发送 Cafe主题v1.0发布 React搭建百度前端技术学院习题演示SPA react-ponitor 1.Two Sum 如何组件化开发WebGIS系统 2016年末总结 代码理解React组件生命周期过程 hexo-theme-cafe
React 与 Redux 实践 —— 城市筛选面板
2017-01-20 · via Giscafer's blog

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。Redux的出现,可以让你构建一致化的应用,运行与不同的环境。

要点

  • 应用中所有的 state 都以一个对象树的形式存储在一个单一的 store 中;
  • 唯一改变 store 的办法是触发 action,一个描述发生什么的对象;
  • 为了描述 action 如何改变 state 树,你需要编写 reducers;
  • 为了UI组件 components 状态无关,你需要编写 containers 来负责管理数据和业务逻辑。

所以,关于组件部分,明智的做法是在最外层上使用 Redux ,然后通过 Props 方式传值给内部子组件,使得components UI组件仅仅是pure render(纯展示)

containers 与 components 区别对比

containers(容器组件) components(UI组件)
Location 最顶层,路由处理 中间和子组件
与Redux联系
读取数据 从 Redux 获取 state 从 props 获取数据
修改数据 从 Redux 派发 action 从 props 调用回调函数

例子

通过一个城市筛选面板来理解 React + Redux 的使用

示例demo:http://blog.giscafer.com/react-demo-list/#/citypanel

源码:https://github.com/giscafer/react-demo-list

编写 React 应用的时候,会有一个大致的开发步骤

  • 构建应用状态树 state 结构
  • 编写 action (描述已发生事件的普通对象,所有修改 state 的操作都必须通过触发action)
  • 编写 reducers (描述 action 如何改变 state tree)
  • 编写 UI组件 components (纯组件,无状态,所有参数通过Props传,可复用性)
  • 编写 容器组件 containers (用来负责管理数据和业务逻辑,react-redux 链接components)
  • 通过 createStore 创建store,通过 Provider 包装根组件

Action 创建常量和函数

定好state tree后,编写actions

actions.jsx

1

2

3

4

5

6

7

8

9

10

11

* citypanel actions

*/

export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';

export function setVisibilityFilter(filter) {

return { type: SET_VISIBILITY_FILTER, filter }

}

Reducers

当应用很大时,可以将它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务,如分页器

reducer 和 action 是好基友

./reducers/cityList.jsx

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

import { SET_VISIBILITY_FILTER } from '../../actions/citypanel/actions';

import { cityData } from './cityData.js';

const initialState = cityData.filter(item => item['citynum'])

console.log(initialState)

export default function cityList(state = initialState, action) {

switch (action.type) {

case SET_VISIBILITY_FILTER:

return cityFilter(action.filter)

default:

return state

}

}

function cityFilter(filter) {

return cityData.map((item) => {

if (filter === '特大' || !filter) {

if (item['citynum']) {

return item;

}

} else if (item['pinyin'][0].toLocaleUpperCase() === filter && !item['citynum']) {

return item;

} else {

console.log(filter)

}

})

}

./reducers/index.jsx 组合所有reducers (多个的时候用)

1

2

3

4

5

6

7

8

9

10

11

import { combineReducers } from 'redux'

import cityList from './cityList'

const rootReducer = combineReducers({

cityList

})

export default rootReducer

UI组件components

宗旨就是pure function

./components/cityList.jsx

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

import React from 'react';

export default ({cityList = []}) => {

return (

<ul className='city-list'>

{

cityList.map((city,index) => {

if(city){

return <li className='left textCenter' key={index} data-name={city.name}>{city.name}</li>

}

})

}

</ul>

);

}

./components/letterFilter.jsx

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import React from 'react';

export default ({onFilterChange}) => {

let letterArr=['特大','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

return (

<ul className='city-index'>

{

letterArr.map((letter,i) => {

return <li className='left textCenter on_mouseover' key={i} onClick={()=>{ onFilterChange(letter) }}>{letter}</li>

})

}

</ul>

);

}

外层组件组合letterFilter与cityList子组件,./components/index.jsx

//此处的函数参数cityList与setVisibilityFilter是由容器组件传输

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import React from 'react';

import LetterFilter from './letterFilter';

import CityList from './cityList';

import '../../styles/citypanel/index.less';

import Nav from '../nav/Nav';

export default ({cityList = [], setVisibilityFilter}) => {

return (

<div className='city-panel'>

<Nav />

<CityList cityList={cityList} />

<LetterFilter onFilterChange={setVisibilityFilter} />

</div>

)

}

容器组件containers

主要是通过react-redux中间件提供的connect方法来链接containerscomponents,而connect 方法提供了两个方法 mapStateToPropsmapDispatchToProps,它们定义了 UI 组件的业务逻辑。前者负责输入逻辑。
mapStateToProps将 state 映射到 UI 组件的参数(Props),mapDispatchToProps负责输出逻辑,即将用户对 UI 组件的操作映射成 Action,也可以通过bindActionCreators方法将action的所有方法绑定到props上。

./containers/App.jsx

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import { bindActionCreators } from 'redux'

import { connect } from 'react-redux'

import cityPanelIndex from '../../components/citypanel/index'

import * as ctiyActions from '../../actions/citypanel/actions'

function mapStateToProps(state){

return {

cityList:state.cityList

}

}

function mapDispatchToProps(dispatch){

return bindActionCreators(ctiyActions,dispatch);

}

export default connect(mapStateToProps,mapDispatchToProps)(cityPanelIndex)

注册store

将 state 和 action 交给 redux 来管理

./stores/createStore.jsx

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

import { createStore, applyMiddleware } from 'redux'

import thunk from 'redux-thunk'

import reducer from '../reducers/citypanel'

const createStoreWithMiddleware = applyMiddleware(

thunk

)(createStore)

export default function configureStore(initialState) {

const store = createStoreWithMiddleware(reducer, initialState)

if (module.hot) {

module.hot.accept('../reducers/citypanel', () => {

const nextReducer = require('../reducers/citypanel')

store.replaceReducer(nextReducer)

})

}

return store

}

App主文件入口

最终Provider包装主组件(containers)

Main.jsx

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

* citypanel主入口

*/

import React from 'react'

import { Provider } from 'react-redux'

import App from '../../containers/citypanel/App'

import configureStore from '../../stores/configureStore'

const store = configureStore()

export default () => {

return (

<Provider store={store}>

<App />

</Provider>

)

}

一个react + redux 应用完成了

演示:http://blog.giscafer.com/react-demo-list/#/citypanel

源码:https://github.com/giscafer/react-demo-list

(完)


参考链接

推荐文章