中文文档地址:https://www.redux.org.cn/
导包:
npm install redux-thunk//这个是用来支持发送异步的action,常见如网络请求:请求前触发请求中的action,等请求拿到回调再去触发请求成功和失败的action
npm install --save react-redux
npm install --save redux
一:三个主要概念:Action、Reducer和store
store:所有的state全部在这里面,只可读,无法修改;
Reducer:页面触发不同的action来返回不同的state,他是实际操作state的;
Action:用来触发Reducer返回的,类似后台的接口
Redux的关键方法:
dispatch(ShopAction.add_goods(goods))
这个方法就是将action发送到store里面的reducer里面,store是必须唯一的,但是reducer可以是多个:
//这个表示把多个reducer连接起来,
const rootReducer = combineReducers({
LoginReducer: LoginReducer,
ShopReducer:ShopReducer,
});
二.react-redux
只使用Redux是不会自动帮你刷新页面的,他只是一个类似于数据服务的东西,就像后台接口,你给他什么他给你返回对应的,但是你页面展示的变更,则需要你自己来实现,所以需要state与Redux建立关系则需要React-redux。
React-Redux的关键:connect
import {connect} from 'react-redux'; // 引入connect函数
这个connect是用来将你reducer里面的的state关联你当前页面的props中的属性,从而实现动态刷新。
export default connect(
(state) => ({
status: state.LoginReducer.status,
responsedUserMessage: state.LoginReducer.responsedUserMessage,
hadAddedGoods:state.ShopReducer.hadAddedGoods,
}),
(dispatch) => ({
login: () => dispatch(LoginAction.login()),
addGoods:(goods)=>{dispatch(ShopAction.add_goods(goods))}
})
)(GoodsPage)
上面的例子中,(state)表示我当前页面的props中的status即是store中的status,下面的(dispatch)表示将这个方法放到props里面,为什么不能在页面需要的地方调用dispatch({type:'type'}):(注:这里就是发送action到reducer,而action实际上是个对象。)呢,因为你在页面里面是拿不到这个dispatch对象的。最后的Frament1表示这个connect和Fragment1进行管理,如果你第二个页面也用这个reducer,那第二个页面的connect继续使用这个reducer即可
使用时调用this.props.methodName()即可。
三:适用场景:
某个页面的state会在多个页面进行关联使用,如对A页面state的增删改会影响页面B的显示
四:例子
最简单的应用场景:
以Android为例,ViewPage里面有3个Fragment,可以联想天猫。
Fragment1是用户可见的第一个页面,这个页面包含用户信息和加入购物车;
Fragment2是购物车页面,用户在Fragment1添加到购物车的需要在这里进行显示;
Fragment3是用户信息页面,如果用户在Fragment3登录了则Fragment1需要显示用户信息,否则就提示登录,而且该页面附带退出功能,退出后Fragment1的登录状态也需要改变。
分析:如果是原生开发,那么当页面重新显示的时候回调用某个方法,在这个方法对本地存储的数据进行读取展示即可。但是RN中页面重新进入并不会触发任何方法,所以这个行不通。
第一种:RN所有显示均依赖于你自己设置的state,能做的就是当Fragment1触发了加入购物车就实时更新Fragment3中的state,这里可以使用广播也可以使用导航器来传值,但是系统开销大而且不便于维护。
第二种:在global中存储你的数据,把global的数据作为显示源,也是可以的
使用Redux:
注:这个例子是参考以下例子:
https://github.com/NextChampion/react-native-redux-navigation-example
这个例子的源代码在
https://github.com/15539158137/Redux_Demo
实际执行效果:
这里分为了二部分部分:用户是否登录以及用户信息和购物车信息
为了方便区分,我使用了两个action文件和2个reducer文件,分别对应用户和购物车。下面代码只展示用户对应的这部分
LoginTypes.js:定义登录所需的action的type信息
//这里是为了方便后期维护吧所有的type单独拉出来,直接用字符串传也无所谓
//其实error和out触发的reducer是相同的,但是为了理解,分开
export const LOGIN_IN = 'LOGIN_IN';
export const LOGIN_IN_ERROR = 'LOGIN_IN_ERROR';
export const LOGIN_OUT = 'LOGIN_OUT';
LoginAction.js:action是一个对象,必须包含‘type’字段,可以按需求添加其他字段
import * as LoginTypes from '../types/LoginTypes';
//登录失败
export function loginFail() {
return {
type: LoginTypes.LOGIN_IN_ERROR,
}
}
//登录成功
export function login_in(responseData) {
return {
type: LoginTypes.LOGIN_IN,
responsedUserMessage: responseData,
}
}
//注销登录
export function login_out() {
return {
type: LoginTypes.LOGIN_OUT
}
}
//如果登录过程中需要显示加载框、提示框,最好把这个操作放在所在页面,因为上传中等等是所在页面独有的state,否则处理弹出框等等很麻烦
export function login() {
console.log('登录方法');
return dispatch => {
// 模拟用户登录
let result = fetch('https://www.baidu.com/')
.then((res) => {
//如果登录接口返回成功,那么把返回的json返回回去
dispatch(login_in({
name: '小明',
age: 12,
image: '..',
}));
}).catch((e) => {
//如果失败,把错误返回回去。
dispatch(loginFail());
})
}
}
LoginReducer.js:是个方法,在方法里面对不同的action返回不同的数据
import * as LoginTypes from "../types/LoginTypes";
//这里只存储需要全局处理的数据,页面独有的state如input内容的state就在本身的页面使用。
const initialState = {
status: '离线',//表示当前的登录状态,在线离线两种状态==用0和1当然更好了
responsedUserMessage: null,//登录后的用户信息
}
export default function LoginReducer(state=initialState, action) {
switch (action.type) {
case LoginTypes.LOGIN_IN:
return {
...state,
status: '在线',
responsedUserMessage: action.responsedUserMessage,
}
break;
case LoginTypes.LOGIN_IN_ERROR:
return {
...state,
status: '离线',
responsedUserMessage: null,
}
break;
case LoginTypes.LOGIN_OUT:
return {
...state,
status: '离线',
responsedUserMessage: null,
}
break;
default:
console.log(state);
return state;
}
}
Fragment1.js
'use strict';
import {Provider} from 'react-redux';
import React, {Component} from 'react';
import {connect} from 'react-redux'; // 引入connect函数
import *as LoginAction from '../actions/LoginAction'; // 引入connect函数
import *as ShopAction from '../actions/ShopAction'; // 引入connect函数
import {Platform, Alert,StyleSheet, Text, View, TouchableOpacity} from 'react-native';
const Dimensions = require('Dimensions'); //必须要写这一行,否则报错,无法找到这个变量
const ScreenWidth = Dimensions.get('window').width;
const ScreenHeight = Dimensions.get('window').height;
class GoodsPage extends React.Component {
render() {
const {login}=this.props;//这是js解构赋值的写法等同于this.props.login
let bean1= {'type':"LOGIN_IN_DOING"};
let bean= {type:"LOGIN_IN_DOING"};
return (
{this.props.status=="在线"?"欢迎你:"+this.props.responsedUserMessage.name:'请登录'}
{
let goods={name:'商品1:购买毫秒数'+new Date().getTime(),type:0};
this.props.addGoods(goods);
}}>
添加商品1到购物车
{
let goods={name:'商品2:购买毫秒数'+new Date().getTime(),type:1};
this.props.addGoods(goods);
}}>
添加商品2到购物车
);
}
shouldComponentUpdate(nextProps, nextState) {
console.log("新的信息store里面的fragment2reducer"+JSON.stringify(nextProps.data));
// 登录完成,切成功登录
if (nextProps.status === '登陆成功' && nextProps.isSuccess) {
return true;
}
return true;
}
};
export default connect(
(state) => ({
status: state.LoginReducer.status,
responsedUserMessage: state.LoginReducer.responsedUserMessage,
hadAddedGoods:state.ShopReducer.hadAddedGoods,
}),
(dispatch) => ({
login: () => dispatch(LoginAction.login()),
addGoods:(goods)=>{dispatch(ShopAction.add_goods(goods))}
})
)(GoodsPage)
注意:关键点在于connect方法,我这个页面同时使用了LoginReducer和ShopReducer
RootReducer.js:将两个reducer合并
import { combineReducers } from 'redux';
import LoginReducer from './LoginReducer';
import ShopReducer from './ShopReducer';
//这个表示把多个reducer连接起来,
const rootReducer = combineReducers({
LoginReducer: LoginReducer,
ShopReducer:ShopReducer,
});
export default rootReducer;
store.js:这里是创建store的方法
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from "../../my_redux_test/reducers/RootReducer";
//这里是因为使用了redux-truk的缘故,如果不用,就使用store.createStore即可
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
export default function configureStore(initialState) {
const store = createStoreWithMiddleware(rootReducer, initialState)
return store;
}