React Native+Redux開發實用教程

期待已久的新教程上線啦!解鎖React Native開發新姿勢,一網打盡React Native最新與最熱技術,點我Get!!!

React Native+Redux開發實用教程

為了幫助大家快速上手在React Native與Redux開發,在這本文中將向大家介紹如何在React Native中使用Redux?,以及一些必備基礎以及高級知識

本文參考了《新版React Native+Redux打造高質量上線App》課程的部分講解,更多關于React Native與Redux的實戰技巧可在《新版React Native+Redux打造高質量上線App》中查看。

那么如何在React Native中使用Redux?呢?

準備工作

根據需要安裝以下組件。

  • 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框架)。通常情況下我們需要將根組件嵌套在 <Provider>標簽 中才能使用 connect() 方法。

class Index extends Component {
    render() {
        return  <Provider store={configureStore()}>
            <AppWithNavigationState />
        </Provider>
    }
}

以上代碼片段的完整部分可以在課程源碼中查找。

在上述代碼中我們用 <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

    /*
     * 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 }
    }
    

    以上代碼片段的完整部分可以在課程源碼中查找。

  • 創建reducer

    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。

  • 創建store

    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。

  • 包裝 component:

    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作為參數傳給connectconnect會返回一個生成組件函數,然后我們將App組件當做參數傳給這個函數。

connect是一個高階函數,首先傳入mapStateToProps、mapDispatchToProps,然后返回一個生產 Component 的函數(wrapWithConnect),然后再將真正的Component作為參數傳入wrapWithConnect(MyComponent),這樣就生產出一個經過包裹的Connect組件

  • 發送(dispatch)action

       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分發給reducerreducer會創建當前state的副本,然后修改該副本并返回一個新的state,這樣一來store樹將被更新,然后對應組件的props將被更新,從而組件被更新;

總結

  • Redux 應用只有一個單一的 store。當需要拆分數據處理邏輯時,你應該使用 reducer 組合 而不是創建多個 store;
  • redux一個特點是:狀態共享,所有的狀態都放在一個store中,任何component都可以訂閱store中的數據;
  • 并不是所有的state都適合放在store中,這樣會讓store變得非常龐大,如某個狀態只被一個組件使用,不存在狀態共享,可以不放在store中;

未完待續

參考

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容