在(1)中我講了一個電燈的例子,用開關(guān)來控制等的狀態(tài)的變化原环,
現(xiàn)在把它用react-native/redux來實現(xiàn)了箕肃,比counter還要簡單,只有一個狀態(tài)的改變粹懒,都已經(jīng)寫好了重付,還可以作為一個框架來使用。安裝了redux-logger以后凫乖,可以在按下按鈕的時候?qū)崟r的看到狀態(tài)的變化确垫。同時結(jié)合在UI組件中的console.log的方法,基本解決了redux調(diào)試的方法問題帽芽。在Component中導入了react-native-elements組件庫删掀。這個和bootstrap的效果類似,封裝了很多的組件可以直接來使用导街,這個組件庫可以換為native-base組件庫披泪。在組件中修改UI 完全不改變app的交互操作的邏輯。就這個簡單的app就做了一下午搬瑰,主要原因是一個也是初學款票,另外一個問題為了調(diào)試加進去了好多的console.log(),完全搞亂的出現(xiàn)問題的原因,但是這個坑還是值得的泽论。主要的目的就是給需要學習redux的同學提供一個簡單的教程徽职。redux的學習也要不斷的反復拿捏才行,理解以后其實覺得redux的思想其實很簡單佩厚,但是要發(fā)生這個轉(zhuǎn)變需要一定的時間姆钉,當然有基礎(chǔ)的高手除外。
界面就是這么一個抄瓦,有一個燈潮瓶,下面有一個開關(guān),要解決的問題就是按下按鈕的操作使燈的狀態(tài)發(fā)生變化钙姊,Component里面有兩個文件毯辅,一個是直接在組件中操作,另外一個就是我們這里的主題煞额,組件中操作以后思恐,state在redux中繞了一圈以后回到組件中沾谜,組件的狀態(tài)就發(fā)生了變化,這里組件和redux是分開的胀莹,在學習的時候等你體會到這種分開的情況就離理解差不多了基跑。
要時刻注意的是流程,組件和redux的銜接
1. 組件的操作是怎么在Redux中對上暗號描焰,接上頭的
2.Redux中狀態(tài)發(fā)生改變之后又是怎么返回的組件的
以上兩點就是connect的內(nèi)容媳否,關(guān)鍵就在這里。
下面是文件的結(jié)構(gòu)荆秦,稍等我放到github上去篱竭。
下面先從視覺組件開始
先貼一下直接在組件中操作的邏輯例子
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView,
} from 'react-native';
import * as lampAction from '../actions/lamp';
//import {Container, Header, Title, Content,Button } from 'native-base';
import {
Button
} from 'react-native-elements'
class Lamp extends Component {
constructor(props) {
super(props);
this.state = { //初始化狀態(tài)
light: false
}
}
componentDidMount() {}
shut() { //組件中直接操作的函數(shù),負責改變狀態(tài)
this.setState({
light : !this.state.light //狀態(tài)發(fā)生翻轉(zhuǎn)
})
}
render() {
this.shut=this.shut.bind(this);
//console.log(light); //可以在組件中打印state
//light==false?(uri=' ./image/off.png'):(uri=' ./image/on.png');
var status = this.state.light==false
? require('./image/off.png')
: require('./image/on.png');
return (
<View style={styles.container}>
<Image source={status}
style={styles.customimg}
/>
<Button
style={styles.buttonMargin}
raised
onPress={ this.shut}
title='開關(guān)' />
</View>
);
}
}
var styles=StyleSheet.create({
//樣式省略
});
export default Lamp;
下面是作為導入redux以后的組件代碼
//Lamp/src/component/Lamp.js
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
ListView
} from 'react-native';
import {
Button,
Icon,
SocialIcon
} from 'react-native-elements'; //導入的elements組件庫
//你可以選擇其他的組件庫步绸,加速開發(fā)流程掺逼。
class Lamp extends Component {
constructor(props) {
super(props);
componentDidMount() {}
render() {
//下面這一句是理解的難點,在react中組件都是在使用的時
//候瓤介,通過注入props來獲取一些參數(shù)坪圾,下面這兩個參數(shù),一個是操
//作的方法名,一個是燈的狀態(tài)惑朦,初學的時候很難理解這兩個東西
//到底是從哪里來的。其實有點遠漓概,組件獲取這兩個東西的地方是
//在container文件夾中漾月,container文件其實做的內(nèi)容很少,就是
//要建立組件和Redux之間的聯(lián)系胃珍,這兩個東西也就是在那里注入進來的
const { switchLamp,light} = this.props;
console.log(light.light); //這個語句可以隨時監(jiān)測light的狀態(tài)是
//false還是true.
//下面這個三元判斷就是根據(jù)light的狀態(tài)來決定加載哪一張圖片
var status =light.light==false
? require('./image/off.png')
: require('./image/on.png');
return (
<View style={styles.container}>
<Image source={status}
style={styles.customimg}/>
<Icon
raised
name='heartbeat'
type='font-awesome'
color='#f50'
//這個地方的switchLamp是函數(shù)梁肿,然而這個函數(shù)并不在這里執(zhí)
//行,我在container里面吧這個函數(shù)綁定到了dispatch上
//這樣觅彰,執(zhí)行這個函數(shù)吩蔑,綁定地就會執(zhí)行一個dispatch(),dispatch的意思
//是分發(fā),那么分發(fā)這個函數(shù)到什么地方呢填抬?這個函數(shù)和action
//中一個函數(shù)名字是一樣的烛芬,這樣發(fā)生在組件中的函數(shù)動作就會和
//redux中的函數(shù)名配對了,redux就感知到了組件要干什么飒责,所謂
//對上眼了赘娄,對上暗號了。這里我們約定好函數(shù)名只由redux中的
//action來定義宏蛉,組件中直接來用遣臼,這樣就不會發(fā)生錯誤匹配的問題
//這里的onPress函數(shù)式唯一和操作邏輯相關(guān)的地方,如果修改組
//件樣式拾并,或者更換組件揍堰,只保留這個函數(shù)就可以了鹏浅,其他的都可
//樣式和邏輯徹底分開了,當然這是理想化的屏歹,實際操作的是后隐砸,
//也是挺復雜的。
onPress={() => switchLamp()} />
</View>
);
}
}
var styles=StyleSheet.create({
//樣式代碼省略
});
export default Lamp;
既然上面的函數(shù)已經(jīng)和Redux已經(jīng)對上眼了西采,我們就馬上看看凰萨,
他們是怎么對上的
//lamp/src/containers/App.js
import { bindActionCreators } from 'redux'; //用來綁定函數(shù)和dispatch()
import { connect } from 'react-redux'; //redux和component的中間代理人
import Lamp from '../components/Lamp'; //這就是上面講的組件
import * as switchLamp from '../actions/lamp'; //獲取actions中
//定義的action函數(shù),這里并不是要這些函數(shù)做什么,而是給組件一套對暗號的密碼表械馆,
//mapStateToProps,看名字就是State轉(zhuǎn)變?yōu)镻rops胖眷,state是在
//redux中的,組件在使用的時候是通過props來獲取參數(shù)的,
//所以這里有這么一個轉(zhuǎn)變的過程
function mapStateToProps(state, props) {
//console.log(state);
return {
light:state.light //這里的state比較簡單霹崎,可以很復雜
};
}
//這里把方法也轉(zhuǎn)為props珊搀,以供組件使用
function mapDispatchToProps(dispatch) {
//使用bindActionCreators來綁定dispatch和函數(shù)
//這是switchLamp看起來是一個函數(shù),其實是一個對象尾菇,
//包含了actions里定義的所有函數(shù)
//組件中的函數(shù)就會自動dispatch到actions中和對應的函數(shù)想匹配境析,dispatch以后redux就接管了后續(xù)的邏輯操作
return bindActionCreators(switchLamp, dispatch);
}
//connect是我們這個系列要講的核心,所有的內(nèi)容其實都是圍繞
//他派诬,盡管這一句劳淆,但是是非常重要的,經(jīng)過這么connect以后
//組件就獲取了所需的方法名和props,
//connect其實是很靈活的默赂,沒有規(guī)定只能connect一次沛鸵,
//所有的組件都可以用他來包裝,看其他代買的時候要注意
//因為一旦程序規(guī)模變大缆八,action里面的函數(shù)就不好一次注入了曲掰,
//在不同的組件中注入自己需要的函數(shù)和props是很好的選擇。
export default connect(mapStateToProps, mapDispatchToProps)(Lamp);
組件dispatch 一個函數(shù)以后奈辰,redux就接管了后續(xù)的操作栏妖。 進入到actions中
//actions/lamp.js
export const SWITCH = 'SWITCH'; //這個常量是給reducer用的
//在redux中有幾個地方可以打印state看看,但是這個文件中
//絕對不能打印state
import React, { Component } from 'react';
import {SWITCH} from '../constants/ActionTypes';//也可以在這里定義常量
//下面的這個函數(shù)和組件dispatch的函數(shù)名是一樣的時候奖恰,就會執(zhí)
//行操作吊趾,這個操作拿著type的類型去操作state的改變。
//你看凡是邏輯分離的地方都要對這個信號
export function switchLamp() {
return {
type: SWITCH,
};
};
上面的type 就會和redcer匹配瑟啃,reducer的作用是改變state趾徽,
state對象其實就相當于一個數(shù)據(jù)庫,用來存儲app的所有變化翰守。
在redux中把state定義為一個??孵奶,樹有各個部分,果實可以吃蜡峰,
葉子可以做藥材了袁,樹皮可以做電纜朗恳,樹干可以蓋房子,樹根可以做根雕载绿。 當你要吃果實的時候只需要去摘果實就可以了粥诫,并不需要把整個樹都弄到,所以需要進行過濾崭庸。 這個過程是在container中來完成的怀浆。我們的這個實例比較簡單,只是一顆種子怕享,么辦法過濾执赡。
下面就看看reducer做了什么工作
//lamp/reducers/lamp.js
//先導入actiontype,這樣從aciton過來的動作函筋,reducer就知道要干什么
import { SWITCH } from '../constants/ActionTypes';
//初始化一顆樹沙合,先種下一顆種子,隨著程序的變大跌帐,這棵樹也變大
const initialState = {
light: false
};
//實際的操作就在這里了首懈,
export default function light(state=initialState, action) {
switch (action.type) { //進行匹配
case SWITCH: //匹配上了
return {
light:!state.light, // 返回一個新的狀態(tài),取反就可以了
//ui 組件中就可以根據(jù)這個變化來改變狀態(tài)
};
default:
return state;
};
}
//有時候程序較復雜,reducer寫在一起很難理解谨敛,所以就會分開
//最終會合并成一個
//lamp/reducers/index.js
import { combineReducers } from 'redux';
//import film from './film';
import light from './lamp';
const rootReducer = combineReducers({
light
});
export default rootReducer;
組件所需要的邏輯操作就在reducers中完成了究履。
reducer改變的邏輯交給store,這個store其實不實際干活脸狸,而是
一個發(fā)號施令的角色最仑。所有的操作和所有的狀態(tài)變化他都知道,
所以我們可以在這里監(jiān)控整個程序的變化肥惭,redux-logger在store里面注入就可以監(jiān)視變化了
//看起來比較復雜
//這里面還注入了redux-sage這個是用來進行異步操作的邏輯
//在這里涉及不到。
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware, { END } from 'redux-saga';
import rootReducer from '../reducers/index'; 注入reducer到store
const middlewares = [];
const createLogger = require('redux-logger');
// configuring saga middleware
const sagaMiddleware = createSagaMiddleware();
middlewares.push(sagaMiddleware);
if (process.env.NODE_ENV === 'development') {
const logger = createLogger();
middlewares.push(logger);
}
const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore);
export default function configureStore(initialState) {
const store = createStoreWithMiddleware(rootReducer, initialState);
// install saga run
store.runSaga = sagaMiddleware.run;
store.close = () => store.dispatch(END);
return store;
}
下面我們在返回container,reducer中改變的狀態(tài)有要返回組件中
我把container的代碼再粘貼一遍紊搪,但是注意這一次的container
和剛開始的container實際是不同的蜜葱,state發(fā)生了改變了。
//lamp/src/containers/App.js
import { bindActionCreators } from 'redux'; //用來綁定函數(shù)和dispatch()
import { connect } from 'react-redux'; //redux和component的中間代理人
import Lamp from '../components/Lamp'; //這就是上面講的組件
import * as switchLamp from '../actions/lamp'; //獲取actions中
//定義的action函數(shù),這里并不是要這些函數(shù)做什么耀石,而是給組件一套對暗號的密碼表牵囤,
//mapStateToProps,看名字就是State轉(zhuǎn)變?yōu)镻rops,state是在
//redux中的,組件在使用的時候是通過props來獲取參數(shù)的滞伟,
//所以這里有這么一個轉(zhuǎn)變的過程
function mapStateToProps(state, props) {
//console.log(state);
return {
light:state.light //這里的state比較簡單揭鳞,可以很復雜
};
}
//這里把方法也轉(zhuǎn)為props,以供組件使用
function mapDispatchToProps(dispatch) {
//使用bindActionCreators來綁定dispatch和函數(shù)
//這是switchLamp看起來是一個函數(shù)梆奈,其實是一個對象野崇,
//包含了actions里定義的所有函數(shù)
//組件中的函數(shù)就會自動dispatch到actions中和對應的函數(shù)想匹配,dispatch以后redux就接管了后續(xù)的邏輯操作
return bindActionCreators(switchLamp, dispatch);
}
//connect是我們這個系列要講的核心亩钟,所有的內(nèi)容其實都是圍繞
//他乓梨,盡管這一句鳖轰,但是是非常重要的,經(jīng)過這么connect以后
//組件就獲取了所需的方法名和props,
//connect其實是很靈活的扶镀,沒有規(guī)定只能connect一次蕴侣,
//所有的組件都可以用他來包裝,看其他代買的時候要注意
//因為一旦程序規(guī)模變大臭觉,action里面的函數(shù)就不好一次注入了昆雀,
//在不同的組件中注入自己需要的函數(shù)和props是很好的選擇。
export default connect(mapStateToProps, mapDispatchToProps)(Lamp);
至此組件中的操作數(shù)據(jù)流程就完成了蝠筑。
這個是比較簡單的狞膘,但是redux的基本流程和框架就是這樣的。
我在(1)中講了菱肖,因為組件和redux是解耦和客冈,改變?nèi)我庖粋€,另一個不發(fā)生變化就可以很好的工作稳强。下面我們能不能再進一步场仲?
燈的開關(guān)也可以換成聲控的,有聲音以后會延遲一段時間再滅退疫,
如果繼續(xù)有聲音渠缕,燈還會亮著。能實現(xiàn)嗎褒繁?