期待已久的新教程上線啦!解鎖React Native開發新姿勢,一網打盡React Native最新與最熱技術,點我Get!!!
為了幫助大家快速上手在React Native與Redux開發,在這本文中將向大家介紹如何在React Native中使用Redux?,以及一些必備基礎以及高階知識。
本文參考了《新版React Native+Redux打造高質量上線App》課程的部分講解,更多關於React Native與Redux的實戰技巧可在《新版React Native+Redux打造高質量上線App》中檢視。
準備工作
根據需要安裝以下元件。
- redux(必選)
- react-redux(必選):redux作者為方便在react上使用redux開發的一個使用者react上的redux庫;
- redux-devtools(可選):Redux開發者工具支援熱載入、action 重放、自定義UI等功能;
- redux-thunk:實現action非同步的middleware;
- redux-persist(可選):支援store本地持久化;
- redux-observable(可選):實現可取消的action;
npm install --save redux
npm install --save react-redux
npm install --save-dev redux-devtools
複製程式碼
react-redux介紹
react-redux
是Redux 官方提供的 React 繫結庫。 具有高效且靈活的特性。
檢視層繫結引入了幾個概念:
<Provider>
元件: 這個元件需要包裹在整個元件樹的最外層。這個元件讓根元件的所有子孫元件能夠輕鬆的使用 connect() 方法繫結 store。connect()
:這是 react-redux 提供的一個方法。如果一個元件想要響應狀態的變化,就把自己作為引數傳給 connect() 的結果,connect() 方法會處理與 store 繫結的細節,並通過 selector 確定該繫結 store 中哪一部分的資料。selector
:這是你自己編寫的一個函式。這個函式宣告瞭你的元件需要整個 store 中的哪一部分資料作為自己的 props。dispatch
:每當你想要改變應用中的狀態時,你就要 dispatch 一個 action,這也是唯一改變狀態的方法。
react-redux提供以下API:
- Provider
- connect
Provider
API原型:
<Provider store>
使元件層級中的 connect() 方法都能夠獲得 Redux store(將store傳遞給App框架)。通常情況下我們需要將根元件巢狀在 標籤 中才能使用 connect() 方法。
class Index extends Component {
render() {
return <Provider store={configureStore()}>
<AppWithNavigationState />
</Provider>
}
}
複製程式碼
以上程式碼片段的完整部分可以在課程原始碼中查詢。
在上述程式碼中我們用 標籤包裹了根元件AppWithNavigationState
,然後為它設定了store引數,store (Redux Store)接受的是應用程式中唯一的 Redux store 物件。
connect
API原型:
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
連線 React 元件與 Redux store,連線操作會返回一個新的與 Redux store 連線的元件類,並且連線操作不會改變原來的元件類。
react-redux
提供了connect函式,connect是一個高階函式,首先傳入mapStateToProps、mapDispatchToProps,然後返回一個生產 Component 的函式(wrapWithConnect),然後再將真正的Component作為引數傳入wrapWithConnect(MyComponent),這樣就生產出一個經過包裹的Connect元件,如:export default connect(mapStateToProps)(HomePage);
使用步驟
-
/* * action 型別 */ export const ADD_TODO = 'ADD_TODO'; export const COMPLETE_TODO = 'COMPLETE_TODO'; export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER' /* * 其它的常量 */ export const VisibilityFilters = { SHOW_ALL: 'SHOW_ALL', SHOW_COMPLETED: 'SHOW_COMPLETED', SHOW_ACTIVE: 'SHOW_ACTIVE' }; /* * action 建立函式 */ export function addTodo(text) { return { type: ADD_TODO, text } } export function completeTodo(index) { return { type: COMPLETE_TODO, index } } export function setVisibilityFilter(filter) { return { type: SET_VISIBILITY_FILTER, filter } } 複製程式碼
以上程式碼片段的完整部分可以在課程原始碼中查詢。
-
import { combineReducers } from 'redux' import { ADD_TODO, COMPLETE_TODO, SET_VISIBILITY_FILTER, VisibilityFilters } from './actions' const { SHOW_ALL } = VisibilityFilters function visibilityFilter(state = SHOW_ALL, action) { switch (action.type) { case SET_VISIBILITY_FILTER: return action.filter default: return state } } function todos(state = [], action) { switch (action.type) { case ADD_TODO: return [ ...state, { text: action.text, completed: false } ] case COMPLETE_TODO: return [ ...state.slice(0, action.index), Object.assign({}, state[action.index], { completed: true }), ...state.slice(action.index + 1) ] default: return state } } //通過combineReducers將多個reducer合併成一個rootReducer const todoApp = combineReducers({ visibilityFilter, todos }) export default todoApp 複製程式碼
以上程式碼片段的完整部分可以在課程原始碼中查詢。
這裡通過Redux提供的combineReducers
方法,將多個reducer聚合成一個rootReducer。
-
import {createStore} from 'redux' import todoApp from './reducers' let store = createStore(todoApp) 複製程式碼
這裡通過Redux提供的createStore
方法,建立了store;
-
使用
<Provider>
包裹根元件import {Provider} from 'react-redux' export default class index extends Component { render() { return (<Provider store={store}> <App/> </Provider>) } } 複製程式碼
以上程式碼片段的完整部分可以在課程原始碼中查詢。
這裡我們使用react-redux
提供的<Provider>
來包裹我們的根元件,讓根元件的所以子元件都能使用 connect() 方法繫結 store。
-
function selectTodos(todos, filter) { switch (filter) { case VisibilityFilters.SHOW_ALL: return todos case VisibilityFilters.SHOW_COMPLETED: return todos.filter(todo => todo.completed) case VisibilityFilters.SHOW_ACTIVE: return todos.filter(todo => !todo.completed) } } // Which props do we want to inject, given the global state? // Note: use https://github.com/faassen/reselect for better performance. function select(state) { return { visibleTodos: selectTodos(state.todos, state.visibilityFilter), visibilityFilter: state.visibilityFilter } } // 包裝 component ,注入 dispatch 和 state 到其預設的 connect(select)(App) 中; export default connect(select)(App) 複製程式碼
以上程式碼片段的完整部分可以在課程原始碼中查詢。
通過上述程式碼我們宣告App
元件需要整個 store 中的哪一部分資料作為自己的 props,這裡用到了connect
,我們將select
作為引數傳給connect
,connect
會返回一個生成元件函式,然後我們將App元件當做引數傳給這個函式。
connect是一個高階函式,首先傳入mapStateToProps、mapDispatchToProps,然後返回一個生產 Component 的函式(wrapWithConnect),然後再將真正的Component作為引數傳入wrapWithConnect(MyComponent),這樣就生產出一個經過包裹的Connect元件
-
render() { // Injected by connect() call: const {dispatch, visibleTodos, visibilityFilter} = this.props return ( <View> <AddTodo onAddClick={text => dispatch(addTodo(text)) } toast={this.toast} /> <TabBar filter={visibilityFilter} onFilterChange={nextFilter => dispatch(setVisibilityFilter(nextFilter)) }/> <TodoList todos={visibleTodos} onTodoClick={index => dispatch(completeTodo(index)) } toast={this._toast()} /> <Toast ref={toast => this.toast = toast}/> </View> ) } 複製程式碼
以上程式碼片段的完整部分可以在課程原始碼中查詢。
在這裡我們通過dispatch
將action傳送到store,store會將這個action
分發給reducer
,reducer
會建立當前state的副本,然後修改該副本並返回一個新的state,這樣一來store樹將被更新,然後對應元件的props將被更新,從而元件被更新;
總結
- Redux 應用只有一個單一的 store。當需要拆分資料處理邏輯時,你應該使用 reducer 組合 而不是建立多個 store;
- redux一個特點是:狀態共享,所有的狀態都放在一個store中,任何component都可以訂閱store中的資料;
- 並不是所有的state都適合放在store中,這樣會讓store變得非常龐大,如某個狀態只被一個元件使用,不存在狀態共享,可以不放在store中;