若是要看理論的童鞋點擊這裏 redux中文文檔 或者 redux官方文檔 ,本文不會太刻意去介紹大篇幅的理論,本文不作框架之間的對比,只給想學redux
的童鞋提供實質的、高效的、易理解的學習參考資源,分享本身在學習過程當中的獲得。文章更新完後會比較長,請耐心閱讀理解,仔細品味。不熟悉redux
也不要緊,能夠跟着文章思路,將三個demo
敲完,相信你必定獲益匪淺。(文後有彩蛋 )。javascript
redux
基本使用 (附demo)redux
Middleware
使用(附demo)redux
集成 navigation
(附demo)react native
版。
react native
版。
集成react-native-navigation
後把前兩個domo
綜合。html
有的童鞋可能會有疑問前端
問:官方不是Todos
demo嗎?爲何還要寫這個demo?java
答:官方的demo都是react
的,而並不是react native
的。我也找過不少關於介紹redux
的文章,但我發現找到的資料要麼太基礎、要麼介紹不全面、提供的demo下載沒法使用等等各類問題,迫使我有了本身動手造輪的衝動,並且這個demo
並不是只是介紹關於redux
的基礎的東西,而是經過三套demo
實踐連貫的圖文的方式,讓讀者更好的理解,後面還會陸續更新在使用redux
過程當中的獲得,但願你們鼓勵支持。react
一般一個大項目到後期是須要不少開發者參與的,若是每一個開發者都使用本身的一套代碼規範作事情,這樣帶來的後果就是:後期的代碼管理工做帶來很是大的麻煩,浪費更多的時間去重構,並且也會讓新人看代碼時理解花更多的時間,還容易把別人帶溝裏去,因此一個大型項目最初構建架構的時候就必需要遵照一些規範。android
那麼咱們怎麼能敲出清爽而又優雅的代碼呢?又如何檢查咱們代碼質量合格呢? 我在這裏極力推薦遵照airbnb/javascript的規範和使用eslint來檢查本身代碼的代碼質量(是否遵照了規範),由於它們已經獲得了不少公司和開發者的承認。(這裏過多的介紹airbnb
eslint
,本文只提供思路,想了解更多自行搜索)ios
在沒有使用代碼規範前咱們可能用各自的風格寫了不少年的代碼了,忽然要適應這套規範可能很是不適應,不要緊,多敲多練習,時間長了就習慣了,誰尚未一個過程,過程是痛苦的,但痛苦事後會給你帶來質的昇華,本身慢慢領悟體會吧。好的事物東西是會被世界所接受,差的事物最終是要被替代的,因此作爲一個合格的程序員(特別是前端程序員)要擁抱變化,由於它會使你變得更加的優秀,獲得大衆的承認,除非你不肯意讓本身變得更優秀。git
兩張圖示意: 程序員
單一數據源: 整個應用的 state 被儲存在一棵 object tree 中,而且這個 object tree 只存在於惟一一個 store 中。github
State 是隻讀的:惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。
使用純函數來執行修改:爲了描述 action 如何改變 state tree ,你須要編寫 reducers。
預見性:全部的用戶的行爲都是你提早定義好的。
統一管理state:全部的狀態都在一個store中分配管理。
這裏只針對react native
開發而言:
初級:剛接觸react native
我很是不建議去使用,由於你還不知道怎麼用它,建議先達到中級。
中級:使用react native
作出一個以上已經上架的不復雜
的應用 redux
,也能夠不使用,由於使用它並不能讓你在前期快速的迭代開發,在這樣的項目下使用redux
就比如大炮打蚊子
,反作用很大。可是能夠先了解起來,並發現它的優勢。這類相對簡單的應用:當用戶觸發一個動做(程序須要setState({xxx:xxx})
)的時候應用程序狀態流程是這樣的:
高級:使用react native
作出一個以上已經上架的複雜
的應用(涉及到即時通信、界面佈局比較複雜,組件嵌套太多層次等),而這類複雜應用:當用戶觸發一個動做(程序須要setState({xxx:xxx})
)的時候應用程序狀態流程是這樣的:
這種狀態帶來的後果,兩方面分析:
state
狀態,那你是如何有效的管理這些狀態?是什麼緣由致使UI屢次渲染?是哪一步操做致使的UI組件的變化?在沒有使用redux
前你可能已經發現可使用生命週期函數中的shouldComponentUpdate
來減小子組件中不必的渲染,但終究解決不了狀態管理複雜的難題。 當你使用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>
)
}
}
複製代碼
以上四個文件是自定義的四個UI展現組件,這些組件只定義外觀並不關心數據來源和如何改變。傳入什麼就渲染什麼。若是你把代碼從 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
。 總之:只要記住一句話就能夠了:UI展現組件負責 UI 的呈現,容器組件負責管理數據和邏輯。 有時很難分清到底該使用容器組件仍是展現組件。如這個小的組件:
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
看不太懂,那就跟着代碼分析的思路多敲幾遍代碼,也就理解了,有空我會繼續更新未完成的內容。
離上次更新已經有好幾天了,今天抽空更新的內容是Middleware
(中間件)。
Middleware
是在Actions
和Dispatcher
之間嵌入的爲了解決某些問題、提升咱們開發效率而存在的工具。 下面介紹三種經常使用的中間件:
actions
(本文不作介紹,後續項目複雜後可使用它來建立)。action
日誌。 開啓react native
遠程調試模式,操做demo就能在控制檯看到打印的狀態先後變化。
加入中間件後的示意圖以下:
本次demo
代碼講解爲了減小文章篇幅,只會講解涉及到Middleware
的部分,也就是說 demo
中在reducers
、components
、containers
文件加下新增的文件不會作過多的解釋,若是不理解,能夠返回去把第一次更新的內容再解析一遍。
actions/types.js
新增以下代碼
//請求帖子列表
export const REQUEST_POSTS = 'REQUEST_POSTS';
//帖子返回數據
export const RECEIVE_POSTS = 'RECEIVE_POSTS';
//切換數據源
export const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT';
//使緩存過時失效
export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT';
複製代碼
actions/index.js
新增以下代碼
export const selectSubreddit = subreddit => ({
type: SELECT_SUBREDDIT,
subreddit
});
export const invalidateSubreddit = subreddit => ({
type: INVALIDATE_SUBREDDIT,
subreddit
});
export const requestPosts = subreddit => ({
type: REQUEST_POSTS,
subreddit
});
export const receivePosts = (subreddit, json) => ({
type: RECEIVE_POSTS,
subreddit,
posts: json.data,
receivedAt: Date.now()
});
const fetchPosts = subreddit => dispatch => {
// API 發起請求
dispatch(requestPosts(subreddit));
return fetch(`http://localhost:8081/data/${subreddit}.json`)
.then(response => response.json())
.then(json => {
setTimeout(()=>{
//使用 API 請求結果來更新應用的 state
dispatch(receivePosts(subreddit, json))
},2000);
})
};
const shouldFetchPosts = (state, subreddit) => {
const posts = state.postsBySubreddit[subreddit];
if (!posts) {
return true
}
if (posts.isFetching) {
return false
}
return posts.didInvalidate
};
export const fetchPostsIfNeeded = subreddit => (dispatch, getState) => {
if (shouldFetchPosts(getState(), subreddit)) {
return dispatch(fetchPosts(subreddit))
}
};
複製代碼
釋
以上主要須要注意的是
fetchPosts
返回了一個函數,而普通的Action 建立函數
默認返回一個對象。- 返回的函數的參數是
dispatch
和getState
這兩個Redux
方法,普通的Action 建立函數
的參數是Action
的內容。- 在返回的函數之中,先發出一個
Action
: dispatch(requestPosts(subreddit)),表示操做開始。- 異步操做結束以後,再發出一個
Action
: receivePosts(subreddit, json),表示操做結束。
demo中數據源解釋:
原本打算用官方的 reddit demo API,最終發現官方給出的
demo
請求數據會報錯,因此使用了本地的json數據,延遲兩秒模擬網絡API加載數據的過程。
App.js
import React, { Component } from 'react'
import { applyMiddleware, createStore } from 'redux'
import { createLogger } from 'redux-logger'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import LoadPosts from './js/containers/LoadPosts'
import rootReducer from './js/reducers'
export default class App extends Component {
render() {
const logger = createLogger();
const store = createStore(
rootReducer,
applyMiddleware(thunk, logger)
);
return (
<Provider store={store}>
<LoadPosts/>
</Provider>
);
}
}
複製代碼
釋
相比前一個demo
的App.js
,在createStore
的時候參數有變化,多了一個applyMiddleware(thunk, logger)
中間件的參數。 理解了第一次更新內容的童鞋不難看出,Action
是由store.dispatch
方法發送的。而store.dispatch
方法正常狀況下,參數只能是對象,不能是函數。 爲了解決這個問題,就要使用到中間件redux-thunk
改造store.dispatch
,使store.dispatch
能夠接受函數做爲參數。
注意
有的中間件有次序要求,使用前要查一下文檔。好比,logger就必定要放在最後,不然輸出結果會不正確。
到此本次寫做到此結束。
接着更新關於集成navigation
的集成,若是使用過比較老版本的react native
都知道在react-navigation
沒有興起以前,大多數開發者都使用的官方提供的 Navigator
,直到 react native
v0.44.3 發佈時宣佈已經遺棄Navigator
。
Navigator
或者
react-navigation
,因此我並不知道市場上還有多少相似的導航解決方案,此次經過項目空檔期,又深刻了解了一下,目前市場上比較流行的三款導航器:
這是官方推薦的,在
github
上已有1.35W+
的 🌟,由React Native
社區維護,目前,它是最受歡迎的React Native
導航庫。它徹底用JavaScript
編寫,而不是使用本機API
,它從新建立了一些子集。這個選擇容許用戶定製導航體驗的任何部分,而無需學習iOS
或Android
導航邏輯。由於React Navigation的
大部分邏輯都是在JavaScript
中而不是在本機中運行,因此任何阻止JavaScript
線程的狀況都會形成卡頓顯現。另外說明一下react navigation
的v1版本跟v2版本差異挺大的,若是想了解的童鞋能夠看我前面寫的這篇文章 [react native 強大的navigation V2.0+ ](www.jianshu.com/p/05fd0e9bc…)。
目前官方文檔中已經明確提出:
Warning: in the next major version of React Navigation, to be released in Fall 2018, we will no longer provide any information about how to integrate with Redux and it may cease to work. Issues related to Redux that are posted on the React Navigation issue tracker will be immediately closed. Redux integration may continue to work but it will not be tested against or considered when making any design decisions for the library.
Some folks like to have their navigation state stored in the same place as the rest of their application state. Think twice before you consider doing this, there is an incredibly good chance that you do not need to do this!. Storing your React Navigation state in your own Redux store is likely to give you a very difficult time if you don't know what you're doing.
If your only reason for doing this is that you want to be able to perform navigation actions from outside of your components (eg: from a Redux middleware), you can learn more about this in navigating without the navigation prop.
翻譯:
警告: 在下一個大版本的 React Navigation 中, 將在2018年秋季發佈, 咱們將再也不提供有關如何集成 Redux 的任何信息, 而且它可能會中止使用。 發佈在 React Navigation issue tracker 中有關 Redux 的 issue,也將當即關閉。 Redux 集成可能會繼續工做,但不會在爲 library 做出任何設計決策時進行測試或考慮。
有些人喜歡將他們的 navigation state 存儲在與其餘的應用程序的 state 相同的位置。 在你考慮這樣作以前請三思, 可是有一個很是好的機會, 你能夠不須要這樣作!。 若是你不知道本身要作什麼,將 React Navigation state 存儲在你本身的 Redux store 中可能會會很困難。
若是你這樣作的惟一緣由是但願可以從組件外部執行導航操做 (例如: 從 Redux 中間件), 你能夠了解更多關於 不使用 navigation prop 進行導航 的信息。
翻譯成通俗易懂的話就是:React Navigation
在下個版本中將不會再特地考慮去兼容 Redux
,用是能夠用,可是出了問題須要自行解決。
哎,不理解官方爲何要這麼作,多是減小維護成本吧,可是這樣作無疑是一個不明智但選擇,也說不定會有驚喜,暫時期待一下吧。若是項目中集成了 redux
我我的不太推薦使用React Navigation
。
它是基於
React Navigation
,但提供了與其交互的不一樣API
。在github
上已有7600+
的 🌟,它容許您在一箇中心位置定義場景轉換,而無需傳遞導航器對象,而且能夠在代碼中的任何位置輕鬆訪問。最新的beta版本 - 4,除了其餘更改以外,還介紹了抽屜支持和Mob-X驅動的導航狀態機,它將導航邏輯與表示層分開。
另外一個流行的導航庫是由 Wix 開源團隊開發的
React Native Navigation
,在github
上已經接近9000+
的 🌟,它的最大優點是跨平臺界面覆蓋的100%本機平臺導航,具備開箱即用的Redux
支持。您須要爲
iOS
和Android
單獨配置此軟件包,其中包括連接iOS
庫,更新iOS
標頭搜索路徑,在Android
MainActivity
中擴展SplashActivity
而不是ReactActivity
以及文檔中詳細描述的其餘幾個步驟。完成後,您只須要註冊全部應用程序的屏幕並啓動應用程序。
目前官方文檔中也提出:
Note: example redux is deprecated. Since we did not have enough time and resources to maintain both example projects, we decided to stop maintaining the redux example. This does not mean redux can't be used with react-native-navigation (In fact, we use redux in the Wix app). For a working example project which uses redux with RNN you can refer to JuneDomingo/movieapp.
翻譯:
注意:不推薦使用示例redux。因爲咱們沒有足夠的時間和資源來維護這兩個示例項目,所以咱們決定中止維護redux示例。這並不意味着redux不能與react-native-navigation一塊兒使用(事實上,咱們在Wix應用程序中使用redux)。對於使用帶RNN的redux的工做示例項目,您能夠參考JuneDomingo / movieapp。
綜上所訴:就我的而言,從react navigation
和 react-native-navigation
官方對 Redux
的態度徹底是不同的,至少Wix
內部在使用Redux
。 若是項目中須要使用Redux
,個人第一選擇會是React Native Navigation
,由於它是純原生體驗,並且對Redux
支持很好 。若是在不使用Redux
的項目中,能夠嘗試前兩種導航,這兩種導航體驗也不錯的,很是接近原生體驗了。
本文導航選擇使用 react-native-navigation
,關於react-native-navigation
的集成和API使用請參考官方文檔,若是想了解在 React Navigation
中使用 redux
點這裏 或者 這裏,如下是此次更新改變和新增的文件代碼 index.js
//discard (廢棄)
import { AppRegistry } from 'react-native';
AppRegistry.registerComponent('ReduxForReactNativeDemo', () => App);
複製代碼
//new
import App from './App';
new App();
複製代碼
App.js
import React, { Component } from 'react'
import { applyMiddleware, createStore } from 'redux'
import { createLogger } from 'redux-logger'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import rootReducer from './js/reducers'
import { Navigation } from 'react-native-navigation'
import { registerScreens } from './js/components/screens'
const logger = createLogger();
const store = createStore(
rootReducer,
applyMiddleware(thunk, logger)
);
registerScreens(store, Provider);
export default class App extends Component {
constructor(props){
super(props);
this._startApp();
}
_startApp = () => {
Navigation.startTabBasedApp({
tabs: [
{
label: 'Home',
screen: 'ReduxForReactNativeDemo.HomeScreen',
icon: require('./res/img/ic_home.png'),
// selectedIcon: require('./img/checkmark.png'),
title: 'Home',
overrideBackPress: false,
navigatorStyle: {}
},
{
label: 'Posts',
screen: 'ReduxForReactNativeDemo.PostsScreen',
icon: require('./res/img/ic_news.png'),
// selectedIcon: require('./img/checkmark.png'),
title: 'Posts',
navigatorStyle: {}
}
]
});
}
}
複製代碼
比起上個版本的demo
,整個App.js
文件代碼基本都改了
其餘改變
在components
目錄下新增screens
目錄,該文件夾下放一個一個的界面文件,每一個界面裏面又由多個組件組成。
Group.js
更名爲HomeScreen.js
。PostsDetail.js
、PostsScreen.js
、index.js
,index.js
文件做用是註冊全部界面文件。Posts.js
新增item
點擊事件,點擊後進入列表詳細界面。LoadPosts.js
68
行新增 {...this.props}
,爲了在 Posts.js
裏面能夠經過 this.props
獲取到navigator
。res
資源文件夾。本次結構分析就到這裏了,說下三個demo
版本連貫作下來的感覺吧。講真此次對我本人來講學到不少東西,實踐過程當中也遇到各類問題,查閱海量資源,有不少疑問,最終一一攻破,答案慢慢浮出水面。看過不少demo
千奇百怪的寫法都有,不多見到標準的項目工程結構,大多都是爲了實現效果爲目的,而不能在實際項目中去使用這種項目結構,我文章開始階段我就介紹我了爲何要花這些時間和精力來寫這篇技術文章。我會把這種工程結構運用到之後集成了redux
的項目中。找到一份好的學習資料真的很不容易,若是你也以爲不錯的話,不妨把 ❤️ 點亮,讓更多人發現它。
附上 demo ,歡迎 ❤️⭐️👏 指出錯誤或者發佈本身的看法探討,共勉。🤝
注意
直接 clone
下來運行的話,默認看到是最後一次(v3)更新的內容 demo
, 執行git tag
能夠看到的demo有三個 tag
,若是切換到前兩次更新的 demo
內容:根目錄下執行:
切換到v0.1
git checkout -b dev v0.1
切換到v0.2
git checkout -b dev v0.2