react-native + redux 實踐
一、rn環境搭建
看我的另外一篇文章 http://blog.csdn.net/bluefish89/article/details/77802507
二、rn起步
對於RN的時候,通過https://reactnative.cn/docs/0.50/getting-started.html 可知道,通過react-native引入元件,可在Component中的render方法使用JSX標籤如Text、View、FlatList,通過react-native run-ios
構建,可在iPhone中執行出對應原生控制元件。
先看以下程式碼:
import React, { Component } from 'react' ;
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight
} from 'react-native';
export default class ReduxTest extends Component {
constructor(props) {
super(props);
this.state = {
keyword :'劉德華'
};
}
searchMusic(keyword) {
this .setState({keyword:keyword}
}
render() {
return (
<View style={styles.container}>
<Text>{this.state.keyword}</Text>
<TouchableHighlight underlayColor='#f1c40f'
onPress={()=> this.updateData('更改了state' )}>
<Text>點我</Text>
</TouchableHighlight>
</View>
);
}
}
AppRegistry.registerComponent('ReduxTest', () => ReduxTest);
點選按鈕後,文字內容變成’更改了state’,這是因為呼叫了this.setState,通過該方法更新state,會觸發render()重新更新UI。這就差不多是Rn的使用方法了,state用於儲存資料,通過setState觸發UI重繪,典型的狀態機機制。
三、使用redux
redux中有三個基本概念,Action,Reducer,Store。
1.Action
Actions 是把資料從應用傳到 store 的有效載荷。它是 store 資料的唯一來源。用法是通過 store.dispatch() 把 action 傳到 store。
Action 有兩個作用:
- 用Action來分辨具體的執行動作。比如是create 還是delete?或者是update?
操作資料首先得有資料。比如新增資料得有資料,刪除資料得有ID。。action就是存這些資料的地方。
簡單說,就是用來標識想要進行什麼操作。
action本質就是一個js物件,但約定必須帶有type屬性,如:
{
type: ACTION_A, // 必須定義 type
text
}
一般我們都是使用一個函式來返回這個物件體,稱之為actionCreator:
//actions.js
// actionType
export const ACTION_A = 'ACTION_A';
export const ACTION_B = 'ACTION_B';
// actionCreator
export let actionCreatorSetWord = (text) => {
return {
type: ACTION_A, // 必須定義 type
text
}
};
// actionCreator
export let actionCreatorSetList = (list) => {
return {
type: ACTION_B, // 必須定義 type
list
}
};
//action中介軟體,使dispathc能夠接受function物件
export let getListData = keyword => dispatch =>{
var apiUrl = REQUEST_URL + encodeURIComponent(keyword);
fetch(apiUrl).
then(
(response) => response.json()
).
then(
(responseData) => dispatch(actionCreatorSetList(responseData.data.info))
);
}
這裡的getListData方法,因為請求fetch,是非同步action,因此使用了中介軟體,使dispathc能夠接受該action返回的 帶dispatch形參的fuc函式(一般dispatch只接受js 物件),中介軟體在store建立的時候傳入,見以下。
2.Reducer
Action 只是描述了有事情發生了這一事實,並沒有指明應用如何更新 state。這是 reducer 要做的事情。
reducer要做的是判斷action的type,返回新的state,也就是有(previousState, action) => newState 特徵的函式。
如果reduer有很多個時,可以使用combineReducers將多個進行合併,先看程式碼:
//reducers.js
import { ACTION_A, ACTION_B } from './actions';
import { combineReducers } from 'redux';
let initialState = {
keyword : '我的redux的預設keyword',
list : []
};
//combineReducers使用keyword為key,所以這裡的state 其實是 state.keyword,而不再是整個state
function reducerSetKeyWord(state = initialState.keyword, action) {
switch(action.type) {
case ACTION_A:
// return Object.assign({}, state, action.text);
return action.text;
}
return state;
}
function reducerSetList(state = initialState.list, action) {
switch(action.type) {
case ACTION_B:
// return Object.assign({}, state, action.list);
return action.list;
}
return state;
}
//若執行dispath 兩個 reduxs都會被呼叫
//combineReducers決定了state的結構
let rootReducers = combineReducers({
keyword : reducerSetKeyWord, //key表示了處理state中的哪一部分 reducer方法只需要返回器對應部分的值,若省略key,則取reducer方法名為key
list : reducerSetList,
});
export default rootReducers;
這裡使用了combineReducers,而且他還有一個作用,可以決定state的結構,並且在呼叫reducer(state, action)的時候,引數state預設取的就是其key對應的值
注意:
- 不要修改 state。
- 在 default 情況下返回舊的 state。遇到未知的 action 時,一定要返回舊的 state。
- 如果沒有舊的State,就返回一個initialState,這很重要!!!
3.Store
儲存state的地方,建立Store非常簡單。createStore 有兩個引數,Reducer 和 initialState(可選)。
let store = createStore(rootReducers, initialState);
store有四個方法
- getState: 獲取應用當前State。
- subscribe:新增一個變化監聽器。
- dispatch:分發 action。修改State。
- replaceReducer:替換 store 當前用來處理 state 的 reducer。
react-redux提供了Provider元件,可以用來把根元件包起來,這樣,store就可以傳遞給所有的子元件
子元件中,便可以使用mapStateToProps方法和mapDispatchToProps方法,通過connect把state和dispatch繫結到該元件的props中,
例如,有MyText元件:
//index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux'
import thunk from 'redux-thunk';
import rootReducers from './src/reducers';
import MyText from './src/MyText';
let initialState = {
keyword : '我的神啊',
list : []
};
let store = createStore(
rootReducers,
initialState,
applyMiddleware(thunk),
);
export default class ReduxTest extends Component {
render() {
return (
<Provider store={store}>
<View style={styles.container}>
<MyText/>
</View>
</Provider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
AppRegistry.registerComponent('ReduxTest', () => ReduxTest);
以上程式碼,這裡import thunk from ‘redux-thunk’;引入thunk中間,通過applyMiddleware(thunk)引入。
//MyText.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TextInput,
ScrollView,
Image,
FlatList,
NavigatorIOS,
TouchableHighlight,
Button
} from 'react-native';
import {connect} from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actionCreators from './actions';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection : 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}
});
class MyText extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
}
componentWillUnmount(){
// this.unsubscribe();
}
changKeyword = ()=>{
this.props.actionCreatorSetWord("呼叫actionCreatorSetWord");
this.props.getListData("劉德華");
};
render(){
return (
<View style = {this.styles}>
<Text style = {{flex: 1,backgroundColor:'red',top:20,height:20}}>{this.props.keyword}</Text>
<Button style = {{flex: 1}} onPress = {this.changKeyword} title = "點我" />
<FlatList
style = {{flex: 1,backgroundColor: 'green'}}
data={this.props.datalist}
renderItem={
({item}) => (
<Text>{item.filename}</Text>
)
}
/>
</View>
);
}
}
function mapStateToProps(state) {
return { keyword: state.keyword , datalist:state.list};
}
//若不connect,預設直接返回dispatch
function mapDispatchToProps(dispatch) { //dispatch為store傳過來的
return bindActionCreators(actionCreators, dispatch); //將dispatch繫結到所有的actionCreator上
//@return {actionCreator方法名: (parameter) => dispatch(actionCreator(parameter)))} 本質就是對所有actionCreator的實現套上dispatch,key為Creator名,connect則把這個key繫結到pros上
}
// 最終暴露 經 connect 處理後的元件
export default connect(mapStateToProps,mapDispatchToProps)(MyText);
這裡 mapStateToProps返回的物件會直接設定到props上
mapDispatchToProps方法中使用了一個bindActionCreators方法,可以將actions.js中多個action方法繫結到dispatch上,本質就是對所有actionCreator的實現套上dispatch,key為Creator名,connect則把這個key繫結到pros上,類似{actionCreator方法名: (parameter) => dispatch(actionCreator(parameter)))}
,通過mapDispatchToProps返回,對映到改元件的props上,便可以直接通過this.props.getListData直接呼叫action
4.總結
綜上,可以整理出Store、Reducer、Action三者的關係,store是管理協調著reducer、action、state。
store建立的時候傳入了reducer,提供dispatch方法,因此通過store.dispatch(action)修改state,dispatch知道action該傳給哪個reducer,更新什麼state。所以,到最後,我們的更新操作簡化成dispatch(action)的形式
摘錄參考:
https://github.com/berwin/Blog/issues/4
http://cn.redux.js.org/docs/recipes/reducers/PrerequisiteConcepts.html
https://reactnative.cn/docs/0.50/getting-started.html