内容简介:如果要看理论的童鞋点击这里redux中文文档 或者redux官方文档 ,本文不会太刻意去介绍大篇幅的理论,本文不做框架之间的对比,只给想学老规矩先上图:版。
如果要看理论的童鞋点击这里redux中文文档 或者redux官方文档 ,本文不会太刻意去介绍大篇幅的理论,本文不做框架之间的对比,只给想学 redux
的童鞋提供实质的、高效的、易理解的学习参考资源,分享自己在学习过程中的得到(文后有彩蛋 :smile:)。
老规矩先上图:
看过官方Todos demo的童鞋可能会有点熟悉上图的操作,没错,这就是它的react native
版。
为什么我要写这个demo
有的童鞋可能会有疑问 问:官方不是 Todos
demo吗?为什么还要写这个demo? 答:官方的demo都是 react
的,而并非 react native
的。我也找过很多关于介绍redux的文章,但我发现找到的资料要么太基础、要么介绍不全面、提供的demo下载无法使用等等各种问题,迫使我有了自己动手造轮的冲动,而且这个demo并非只是介绍关于redux的基础的东西,有空我还没陆续更新在使用 redux
过程中的得到,希望大家鼓励支持。
demo采的代码规范
通常一个大项目到后期是需要很多开发者参与的,如果每个开发者都使用自己的一套代码规范做事情,这样带来的后果就是:后期的代码管理工作带来非常大的麻烦,浪费更多的时间去重构,而且也会让新人看代码时理解花更多的时间,还容易把别人带沟里去,所以一个大型项目最初构建架构的时候就必须要遵守一些规范。 那么我们怎么能敲出清爽而又优雅的代码呢?又如何检查我们代码质量合格呢? 我在这里极力推荐遵守 airbnb/javascript 的规范和使用 eslint 来检查自己代码的代码质量(是否遵守了规范),因为它们已经得到了很多公司和开发者的认可。(这里过多的介绍 airbnb
eslint
,本文只提供思路,想了解更多自行搜索) 在没有使用代码规范前我们可能用各自的风格写了很多年的代码了,突然要适应这套规范可能非常不适应,没关系,多敲多练习,时间长了就习惯了,谁还没有一个过程,过程是痛苦的,但痛苦过后会给你带来质的升华,自己慢慢领悟体会。 如果还是坚持我行我素,那我也没办法的,我只能说好的东西是会被世界所接受,差的东西最终是要被淘汰的,所以做为一个合格的程序员(特别是前端程序员)要拥抱变化,因为它会使你变得更加的优秀,得到大众的认可。除非你不愿意让自己变得更优秀。(我回看几年前我写的代码,只能用一坨一坨的来形容 ,不知道你们是否也有这种感脚呢?:grin:)
redux能帮我们做什么
两张图示意:
redux特性
-
单一数据源: 整个应用的 state被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store中。
-
State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
-
使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers。
-
预见性:所有的用户的行为都是你提前定义好的。
-
统一管理state:所有的状态都在一个store中分配管理。
哪些开发者和项目适合用redux
这里只针对 react native
开发而言:
-
初级:刚接触
react native
我非常不建议去使用,因为你还不知道怎么用它,建议先达到中级。 -
中级:使用
react native
做出一个以上已经上架的不复杂
的应用redux
,也可以不使用,因为使用它并不能让你在前期快速的迭代开发,在这样的项目下使用redux
就好比大炮打蚊子
,副作用很大。但是可以先了解起来,并发现它的优点。这类相对简单的应用:当用户触发一个动作(程序需要setState({xxx:xxx})
)的时候应用程序状态流程是这样的: -
高级:使用
react native
做出一个以上已经上架的复杂
的应用(涉及到即时通讯、界面布局比较复杂,组件嵌套太多层次等),而这类复杂应用:当用户触发一个动作(程序需要setState({xxx:xxx})
)的时候应用程序状态流程是这样的:
这种状态带来的后果,两方面分析:
- 性能:祖父子组件之间多余的状态传递,导致宝贵的内存资源浪费,同时界面渲染的速度也会变慢,自然用户体验就变差了。
- 状态管理:当程序不断的迭代,界面布局越来越复杂,必然就会产生许多的
state
状态,那你是如何有效的管理这些状态?是什么原因导致UI多次渲染?是哪一步操作导致的UI组件的变化?在没有使用redux
前你可能已经发现可以使用生命周期函数中的shouldComponentUpdate
来减少子组件中没必要的渲染,但终究解决不了状态管理复杂的难题。 当你使用redux
后,复杂的应用程序状态流程是这样的: 看完上面图文后,是否感觉很好理解我们什么时候要使用redux
呢?这要感谢 @justjavac 文章提供的动图支持。
redux for react native 工作逻辑图
感谢@黑森林工作室作者提供的清晰的逻辑图
redux工程结构分析
我对官方的demo小部分位置做了些改造具体看代码分析:
分工明细
-
js/actions
此文件夹下放内容做的事情是:定义用户行为。 -
js/reducers
此文件夹下放内容做的事情是:响应用户行为,返回改变后的状态,并发送到store
。 -
js/components
此文件夹下放内容做的事情是:自定义的组件。 -
js/containers
此文件夹下放内容做的事情是:把components
文件夹中涉及到状态变化的组件进行第二次封装。 -
App.js
入口文件(store在这里),为什么我要把store定义在这里? 因为它是唯一的,而且必须使用react-redux
提供的Provider
组件包裹入口的其他组件才能使redux
中的store
生效。 -
global.js
存放全局定义的变量、常量、方法等。
需要注意的事
- 一个工程中
redux
的store
是唯一的,不能在多个store
。 - 保持
reducer
纯净非常重要。永远不要在reducer
里做这些操作:
- 修改传入参数;
- 执行有副作用的操作,如
API
请求和路由跳转; - 调用非纯函数,如
Date.now()
或Math.random()
;
- 使用对象展开运算符
...
代替Object.assign()
才是最好的解决方案。 - 组件名首字母要大写,也就是说
components
和containers
文件夹下的文件首字母都要大写。 - 应该尽量减少传递到
action
中的数据(能传单个数据就不传对象,能传对象就不传数组)
//good function todoApp(state = initialState, action) { switch (action.type) { case SET_VISIBILITY_FILTER: return Object.assign({}, state, { visibilityFilter: action.filter }) default: return state } } 复制代码
//best function todoApp(state = initialState, action) { switch (action.type) { case SET_VISIBILITY_FILTER: return { ...state, visibilityFilter: action.filter } default: return state } } 复制代码
#代码详解 js/actions/types.js
//添加列表数据 export const ADD_TODO = 'ADD_TODO'; //筛选 export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'; //文字添加/取消中划线 export const TOGGLE_TODO = 'TOGGLE_TODO'; 复制代码
释:
action定义
为什么我要把用户的 action
(行为)定义单独抽出来写一个 type.js
?
- 方便状态管理。
- 复用性。
js/actions/index.js
import { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, } from './types' let nextTodoId = 0; export const addTodo = text => ({ type: ADD_TODO, id: nextTodoId++, text }); export const setVisibilityFilter = (filter) => ({ type: SET_VISIBILITY_FILTER, filter }); export const toggleTodo = id => ({ type: TOGGLE_TODO, id }); 复制代码
释:
Action 创建函数
Action 创建函数 就是生成 action 的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,使用时最好注意区分。
在 Redux 中的 action 创建函数只是简单的返回一个 action:
js/reducers/todos.js
import { ADD_TODO, TOGGLE_TODO, } from '../actions/types' const todos = (state = [], action) => { let {id, text, type} = action; switch (type) { case ADD_TODO: return [ ...state, { id: id, text: text, completed: false } ]; case TOGGLE_TODO: return state.map(todo => (todo.id === id) ? {...todo, completed: !todo.completed} : todo); default: return state; } }; export default todos; 复制代码
js/reducers/visibilityFilter.js
import { SET_VISIBILITY_FILTER } from '../actions/types' import { visibilityFilters } from '../global' const { SHOW_ALL } = visibilityFilters; const visibilityFilter = (state = SHOW_ALL, action) => { let {type, filter} = action; switch (type){ case SET_VISIBILITY_FILTER: return filter; default: return state } }; export default visibilityFilter; 复制代码
释:
reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state(上面两个文件可以看着两个reducer)。
注意:
-
Redux
首次执行时,state
为undefined
,此时需要设置返回应用的初始state
。 - 每个
reducer
只负责管理全局state
中它负责的一部分。每个reducer
的state
参数都不同,分别对应它管理的那部分state
数据。
js/reducers/index.js
import { combineReducers } from 'redux' import todos from './todos' import visibilityFilter from './visibilityFilter' export default combineReducers({ todos, visibilityFilter }) 复制代码
释:
combineReducers()
所做的只是生成一个函数,这个函数来调用你的一系列 reducer,每个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理,然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象。
表面上看上去 combineReducers()
的作用就是把多个 reducer
合成一个的 reducer
。
js/components/Todo.js
import React, { Component } from 'react' import { Text, TouchableOpacity } from 'react-native' import PropTypes from 'prop-types' export default class Todo extends Component { static propTypes = { onClick: PropTypes.func.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }; render(){ let { onClick, completed, text } = this.props; return ( <TouchableOpacity style={{ flexDirection: 'row', flex: 1, height: 50, alignItems: 'center', justifyContent: 'center', backgroundColor: '#cccccc', marginTop: 10 }} onPress={onClick}> <Text style={{ textDecorationLine: completed ? 'line-through' : 'none'}}>{text}</Text> </TouchableOpacity> ); } } 复制代码
js/components/TodoList.js
import React, { Component } from 'react' import PropTypes from 'prop-types' import { FlatList } from 'react-native' import Todo from './Todo' export default class TodoList extends Component { static propTypes = { todos: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }).isRequired ).isRequired, toggleTodo: PropTypes.func.isRequired }; _renderItem = (data) => { let dataItem = data.item; let { id } = dataItem; let { toggleTodo } = this.props; return ( <Todo {...dataItem} onClick={() => toggleTodo(id)} /> ) }; render() { let { todos } = this.props; return ( <FlatList data={todos} keyExtractor={(item)=>item.id.toString()} renderItem={this._renderItem} /> ) } } 复制代码
js/components/Link.js.js
import React, { Component } from 'react' import PropTypes from 'prop-types' import { TouchableOpacity, Text } from 'react-native' export default class Link extends Component { static propTypes = { active: PropTypes.bool.isRequired, filter: PropTypes.string.isRequired, onClick: PropTypes.func.isRequired }; render() { let { active, filter, onClick } = this.props; return ( <TouchableOpacity style={{ marginLeft: 4, height: 40, flex:1, borderWidth: 1, borderColor: '#ccc', alignItems: 'center', justifyContent:'center' }} onPress={onClick} > <Text style={{fontSize: 10, color: active ? 'black' : '#cccccc'}}>{filter}</Text> </TouchableOpacity> ); } } 复制代码
js/components/Filters.js
import React, { Component } from 'react' import { View, } from 'react-native' import FilterLink from '../containers/FilterLink' import { visibilityFilters } from '../global' const { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } = visibilityFilters; export default class Filters extends Component { render(){ return( <View style={{ flexDirection: 'row', marginTop: 20}}> <FilterLink filter={SHOW_ALL} /> <FilterLink filter={SHOW_COMPLETED} /> <FilterLink filter={SHOW_ACTIVE} /> </View> ) } } 复制代码
释:
以上四个文件是自定义的四个展示组件,这些组件只定义外观并不关心数据来源和如何改变。传入什么就渲染什么。如果你把代码从 Redux 迁移到别的架构,这些组件可以不做任何改动直接使用。它们并不依赖于 Redux。
js/containers/AddTodo.js
import React, { Component } from 'react' import { View, TextInput, Button, } from 'react-native' import { connect } from 'react-redux' import { addTodo } from '../actions' class AddTodo extends Component { constructor(props){ super(props); this.inputValue = ''; } render(){ let { dispatch } = this.props; return ( <View style={{flexDirection: 'row'}}> <TextInput style={{flex:1, borderWidth: 1, borderColor: '#cccccc', textAlign: 'center'}} onChangeText={text => this.inputValue = text} /> <Button title="Add Todo" onPress={() => dispatch(addTodo(this.inputValue))}/> </View> ) } } export default connect()(AddTodo) 复制代码
js/containers/FilterLink.js
import { connect } from 'react-redux' import { setVisibilityFilter } from '../actions' import Link from '../components/Link' const mapStateToProps = (state, ownProps) => ({ active: ownProps.filter === state.visibilityFilter, filterText: ownProps.filter }); const mapDispatchToProps = (dispatch, ownProps) => ({ onClick: () => dispatch(setVisibilityFilter(ownProps.filter)) }); export default connect( mapStateToProps, mapDispatchToProps, )(Link) 复制代码
js/containers/VisibleTodoList.js
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' import { visibilityFilters } from '../global' const { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } = visibilityFilters; const getVisibleTodos = (todos, filter) => { switch (filter) { case SHOW_COMPLETED: return todos.filter(t => t.completed); case SHOW_ACTIVE: return todos.filter(t => !t.completed); case SHOW_ALL: return todos; default: throw new Error('Unknown filter: ' + filter) } }; const mapStateToProps = state => ({ todos: getVisibleTodos(state.todos, state.visibilityFilter) }); const mapDispatchToProps = dispatch => ({ toggleTodo: id => dispatch(toggleTodo(id)) }); export default connect( mapStateToProps, mapDispatchToProps, )(TodoList) 复制代码
释:
以上三个是容器组件,作用是把展示组件连接到 Redux。
有时很难分清到底该使用容器组件还是展示组件。如这个小的组件:
-
AddTodo.js
含有“Add”按钮 和 输入框
技术上讲可以把它分成两个组件,但一开始就这么做有点早。在一些非常小的组件里混用容器和展示是可以的。当业务变复杂后,如何拆分就很明显了。所以现在就使用混合型的吧。
上面出现了使用 react-redux
的 connect()
方法来把展示组件和容器组件关联在一起,这个方法做了性能优化来避免很多不必要的重复渲染。(这样你就不必为了性能而手动实现 React 性能优化建议中的 shouldComponentUpdate
方法。)
使用 connect()
前,需要先定义 mapStateToProps
这个函数来指定如何把当前 Redux store state
映射到展示组件的 props
中。例如, VisibleTodoList
需要计算传到 TodoList
中的 todos
,所以定义了根据 state.visibilityFilter
来过滤 state.todos
的方法,并在 mapStateToProps
中使用。
除了读取 state
,容器组件还能分发 action
。类似的方式,可以定义 mapDispatchToProps()
方法接收 dispatch()
方法并返回期望注入到展示组件的 props 中的回调方法。例如,我们希望 VisibleTodoList
向 TodoList
组件中注入一个叫 onTodoClick
的 props ,还希望 onTodoClick
能分发 TOGGLE_TODO
这个 action
。 最后,使用 connect()
创建 VisibleTodoList
,并传入这两个函数。
js/components/Group.js
import React, { Component } from 'react' import { View } from 'react-native' import AddTodo from '../containers/AddTodo' import Filters from '../components/Filters' import VisibleTodoList from '../containers/VisibleTodoList' export default class Group extends Component { render() { return ( <View style={{paddingHorizontal: 20, paddingVertical: 44}}> <AddTodo/> <Filters/> <VisibleTodoList/> </View> ); } } 复制代码
释:
Group.js 是把所有的关联后的组件串起来,形成一个完整的界面。
App.js
import React, { Component } from 'react' import { createStore } from 'redux' import { Provider } from 'react-redux' import Group from './js/components/Group' import rootReducer from './js/reducers' export default class App extends Component { render() { const store = createStore(rootReducer); return ( <Provider store={store}> <Group /> </Provider> ); } } 复制代码
释:
入口文件传入 Store
- 创建
store
传入reducers
。 - 使用
Provider
组件包裹Group
组件,store
作为属性传入Provider
。
进行到这一步,代码分析完毕。本次写作到此结束。我相信大家如果仔细看完的话,多多少少会有些收获吧,如果demo看不太懂,那就跟着代码分析的思路多敲几遍代码,也就理解了,有空我会继续更新未完成的内容。
彩蛋
附上 github demo ReduxForReactNativeDemo 好使的话,别忘了给出宝贵的:heart::star:️,没有比这个更能鼓舞人心的了 :joy:。最后欢迎:clap:指出错误或者发布自己的见解探讨,共勉。
待更新内容
- Middleware的使用。
- 配合react-navigation使用。
- ......
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Ruby Programming Language
David Flanagan、Yukihiro Matsumoto / O'Reilly Media, Inc. / 2008 / USD 39.99
Ruby has gained some attention through the popular Ruby on Rails web development framework, but the language alone is worthy of more consideration -- a lot more. This book offers a definition explanat......一起来看看 《The Ruby Programming Language》 这本书的介绍吧!
SHA 加密
SHA 加密工具
RGB CMYK 转换工具
RGB CMYK 互转工具