react-native redux 的使用

中文文檔:https://www.redux.org.cn/

結合 阮一峯 大神 redux 教程


1、適用場景:多交互、多數據源

  • 用戶的使用方式複雜
  • 不同身份的用戶有不同的使用方式(比如普通用戶和管理員)
  • 多個用戶之間可以協作
  • 與服務區大量交互,或者使用了WebSocket
  • View要從多個來源獲取數據

2、設計思想

  1. Web應用是一個狀態機,視圖與狀態是一一對應的
  2. 所有的狀態,保存在一個對象裏面

3、基本概念與API

State 的變化會導致 View 的變化,而 Action 就是 View 發出的通知,表示 State 要發生變化了。

也可以理解爲: Action 描述當前發生的事情。改變 State 的唯一辦法,就是使用 Action ,它會運送數據到 Store 。

通過 store.dispatch() 方法將 action 傳到 store。

  • Action 創建函數(注意: “action” 和 “action 創建函數”是兩個概念)

定義一個函數來生成 Action ,這個函數就叫 Action 創建函數

const ADD_TODO = '添加 TODO';

function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}

const action = addTodo('Learn Redux');

上面的 addTodo 函數就是一個 Action 創建函數

Store 收到 Action 後,必須給出一個新的 State,這樣 View 纔會發生變化。這種 State 的計算過程就叫做 Reducer 。

Reducer 是一個純函數,接收舊的 State 和 Action 作爲參數,返回一個新的 State。

const reducer = function (state, action) {
    return newState
}

整個應用的狀態的初始狀態,可以作爲 State 的默認值。例:

const initState = 0;
const reducer = (state = initState, action) => {
    switch(action.type) {
        case 'ADD':
            return state + action.payload;
        default: 
            return false;
    }
}

const state = reducer(1, {
    type: 'ADD',
    payload: 2
})

// console.log(state) => 3
  • 純函數

純函數是函數式編程的概念

1. 不得改寫參數
2. 不能調用系統 I/O 的 API
3. 不能調用 Date.now() 或者 Math.random() 等不純方法

由於 Reducer 是純函數,可以保證同樣的 State 可以得到同樣的 View 。因此 Reducer 函數裏面不能改變 State ,必須返回一個全新的對象,例:

// State 是一個對象
function reducer(state, action) {
    return Object.assign({}, state, {thingToChange})
    // 或者
    return {...stete, ...newState}
}

// State 是一個數組
function reducer(state, action) {
    return [...state, newItem];
}
  • Store 是保存數據的地方,整個應用只有一個 Store。職責:
1. 維持應用的 State
2. 提供 getState() 方法獲取 state
3. 提供 dispatch(action) 方法更新 state
4. 通過 subscribe(listener) 註冊監聽器
5. 通過 subscribe(listener)  返回的函數註銷監聽器

根據已有的 reducer 來創建 store 非常容易

import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

// createStore(fn)

createStore 函數接受另一個函數作爲參數,返回新的 Store 對象。

 

注:更多詳細的內容請看官網或者阮一峯大神的redux入門教程


例: 登錄案例

描述: 使用 redux 進行登錄狀態的管理,使用 redux-persist 進行持久化存儲,react-redux 與 redux 一起使用,react-navigation 頁面之間的跳轉

目錄結構

  • ActionTypes.js
'use strict'

export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_ERROR = 'LOGIN_ERROR';
export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
export const LOGOUT_ERROR = 'LOGOUT_ERROR';
  • LoginAction.js
'use strict'

import {
    LOGIN_SUCCESS,
    LOGIN_ERROR,
    LOGOUT_SUCCESS,
    LOGOUT_ERROR
} from './ActionTypes'

export function loginSuccess(user) {
    return {
        type: LOGIN_SUCCESS,
        isLogin: true,
        message: '登錄成功',
        user
    }
}
export function loginError(user) {
    return {
        type: LOGIN_ERROR,
        isLogin: false,
        message: '登錄失敗',
        user
    }
}
export function logoutSuccess(user) {
    return {
        type: LOGOUT_SUCCESS,
        isLogin: false,
        message: '退出成功',
        user
    }
}
export function logoutError(user) {
    return {
        type: LOGOUT_ERROR,
        isLogin: false,
        message: '退出失敗',
        user
    }
}
  • LoginReducer.js
'use strict'
import _ from 'lodash'

import {
    LOGIN_ERROR,
    LOGIN_SUCCESS,
    LOGOUT_ERROR,
    LOGOUT_SUCCESS,
} from '../actions/ActionTypes'

export default function loginReducer(state = null, action) {
    switch(action.type) {
        case LOGIN_SUCCESS: 
            return _.assign({}, state, {
                isLogin: true,
                message: '登錄成功',
                user: action.user,
            })
        case LOGIN_ERROR: 
            return _.assign({}, state, {
                isLogin: false,
                message: '登錄失敗',
                user: action.user,
            })
        case LOGOUT_SUCCESS: 
            return _.assign({}, state, {
                isLogin: false,
                message: '退出成功',
                user: null,
            })
        case LOGOUT_ERROR: 
            return _.assign({}, state, {
                isLogin: false,
                message: '退出失敗',
                user: action.user,
            })
        default: 
            return state
    }
}
  • RootReducer.js
'use strict'

import { combineReducers } from 'redux'
import LoginReducer from './LoginReducer'

const rootReducer = combineReducers({
    LoginReducer,
})

export default rootReducer
  • store.js
import { createStore } from 'redux'
import { persistReducer, persistStore } from 'redux-persist'
import storage from 'redux-persist/lib/storage' //defaults to localStorage for web and AsyncStorage for react-native

import RootReducer from './reducers/RootReducer'

const persistConfig = {
    key: 'root',
    storage,
}
const persistedReducer = persistReducer(persistConfig, RootReducer)

export const store = createStore(persistedReducer);
export const persistor = persistStore(store);
  • App.js
import React, {Component} from 'react';
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './redux/store'

import { createStackNavigator, createAppContainer } from 'react-navigation'

import Home from './src/Home'
import Login from './src/Login'
 
const RootStack = createStackNavigator(
    {
        Home,
        Login,
    },
    {
        initialRouteName: 'Home',
        defaultNavigationOptions: {
            header: null
        }
    }
)
 
const AppContainer = createAppContainer(RootStack);

export default class Navigation extends Component {
    render() {
        return (
            <Provider store={store}>
                <PersistGate persistor={persistor} loading={null}>
                    <AppContainer/>
                </PersistGate>
            </Provider>
            
        )
    }
}
  • Home.js
import React, {Component} from 'react';
import {
    StyleSheet, 
    Text, 
    View,
    Button,
} from 'react-native';
import { connect } from 'react-redux'
import { logoutSuccess } from '../redux/actions/LoginAction'

class Home extends Component {
    constructor(props) {
        super(props);
        this.state = {
            user: {}
        }
    }
    componentDidMount() {
        let user = this.props.user;
        this.setState({user})
    }
    render() {
        let user = this.props.user;
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>登錄狀態: {user ? '已登錄' : '未登錄'}</Text>
                {
                    user ? 
                    <View>
                        <Text style={styles.welcome}>姓名:{user.name}</Text>
                        <Text style={styles.welcome}>年齡:{user.age}</Text>
                    </View>
                    :
                    null
                }
                
                {
                    user ?
                    <View>
                        <Button title={'退出'}
                            onPress={()=>this.props.dispatch(logoutSuccess())}
                        />
                        <Button title={'前往登錄頁修改信息'}
                            onPress={()=>this.props.navigation.navigate('Login')}
                        />
                    </View>
                    :
                    <Button title={'前往登錄'}
                        onPress={()=>this.props.navigation.navigate('Login')}
                    />
                }
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
});

function selected(state) {
    return user = state.LoginReducer;
}

export default connect(selected)(Home)
  • Login.js
import React, {Component} from 'react';
import {
    StyleSheet, 
    Text, 
    View,
    TextInput,
    Button,
} from 'react-native';
import { connect } from 'react-redux'
import { loginSuccess } from '../redux/actions/LoginAction'

class Login extends Component {
    constructor(props) {
        super(props);
        this.state = {
            user: {
                name: '',
                age: '',
            }
        }
    }
    componentDidMount() {
        let user = this.props.user;
        if(user) {
            this.setState({user})
        }
    }
    render() {
        let user = this.state.user;
        return (
            <View style={styles.container}>
                <TextInput
                    placeholder={'姓名'}
                    defaultValue={user.name}
                    style={styles.input}
                    onChangeText={(text) => this._change('name',text)}
                />
                <TextInput
                    placeholder={'年齡'}
                    defaultValue={user.age}
                    style={{borderWidth:1,borderColor:'gray',padding:5,marginBottom:2}}
                    onChangeText={(text) => this._change('age',text)}
                />
                <Button title={'登錄'} 
                    onPress={this._login}
                />
                <Button title={'前往首頁'} 
                    onPress={()=>this.props.navigation.navigate('Home')}
                />
            </View>
        );
    }
    _change = (key,value) => {
        let user = this.state.user;
        user[key] = value;
        this.setState({user});
        
    }
    _login = () => {
        let user = this.state.user;
        this.props.dispatch(loginSuccess(user));
        this.props.navigation.push('Home')
    }
}

const styles = StyleSheet.create({
    container: {
        marginTop: 50,
        paddingHorizontal: 20,
        backgroundColor: '#F5FCFF',
    },
    input: {
        borderWidth: 1,
        borderColor: 'gray',
        padding: 5,
        marginBottom: 2,
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
});

function selected(state) {
    return user = state.LoginReducer;
}

export default connect(selected)(Login)

實現效果

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章