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)

实现效果

 

 

 

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