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

推荐订阅源

WordPress大学
WordPress大学
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
美团技术团队
F
Fortinet All Blogs
The GitHub Blog
The GitHub Blog
月光博客
月光博客
V
Visual Studio Blog
T
Tailwind CSS Blog
Stack Overflow Blog
Stack Overflow Blog
博客园 - 聂微东
Jina AI
Jina AI
J
Java Code Geeks
Martin Fowler
Martin Fowler
大猫的无限游戏
大猫的无限游戏
Recorded Future
Recorded Future
C
Check Point Blog
腾讯CDC
N
Netflix TechBlog - Medium
aimingoo的专栏
aimingoo的专栏
罗磊的独立博客
Hacker News: Ask HN
Hacker News: Ask HN
SecWiki News
SecWiki News
博客园 - Franky
Hacker News - Newest:
Hacker News - Newest: "LLM"
N
News | PayPal Newsroom
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Security @ Cisco Blogs
W
WeLiveSecurity
The Last Watchdog
The Last Watchdog
Cloudbric
Cloudbric
F
Full Disclosure
The Cloudflare Blog
Y
Y Combinator Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google DeepMind News
Google DeepMind News
MongoDB | Blog
MongoDB | Blog
S
Schneier on Security
Schneier on Security
Schneier on Security
Spread Privacy
Spread Privacy
L
LINUX DO - 热门话题
AI
AI
N
News and Events Feed by Topic
T
Tor Project blog
P
Palo Alto Networks Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
H
Hackread – Cybersecurity News, Data Breaches, AI and More
G
Google Developers 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

(完)


参考链接

推荐文章