react-native,redux,redux-saga组合开发实现
简单的说搞react开发的痛点之一,单向数据流的传递,redux统一管理数据,redux-saga又处理管理了异步调用。
要实现的内容如下,界面
目录结构
首先从请求接口入手,用axios封装请求接口,统一处理请求 axios.js
import axios from 'axios'let defaultConfig = {
timeout: 3000,
}
let instance = axios
class Axios {
constructor(props) {
if (props && typeof props == 'object') {
instance = axios.create(props)
} else {
instance = axios.create(defaultConfig);
}
//拦截
instance.interceptors.request.use((config) => {
return config;
}, (error) => {
console.log(error)
return Promise.reject(error);
});
//日志 响应结果
instance.interceptors.response.use((response) => {
return response.data;
}, (error) => {
console.log(error)
return Promise.reject(error);
})
}
send(params) {
if (!params || typeof params != 'object') {
throw new Error('params is undefined or not an object')
}
if (params.method == 'GET') {
return get(params.url)
} else if (params.method == 'POST') {
return post(params.url, params)
}
}
}
async function get(url, callback) {
try {
let response = await instance.get(url)
return response
} catch (e) {
console.log(e)
}
}
async function post(url, params, callback) {
try {
let response = await instance.post(url)
//eturn callback(response)
return response
} catch (e) {
console.log(e)
}
}
export default Instance = new Axios();
store.js 管理以及开发环境下的及时更新const sagamiddleware = createSagaMiddleware();export default function configureStore(initStore = {}) {
const middlewares = [sagamiddleware];
if (__DEV__) {
middlewares.push(logger)
}
const createStoreMiddleware = applyMiddleware(...middlewares)(createStore);
const store = createStoreMiddleware(
createReducer(), initStore
);
store.runSaga = sagamiddleware.run;
store.close = () => store.dispatch(END);
// Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
module.hot.accept(() => {
const nextRootReducer = require('../reducers/index').default; //reducers 文件下的 index
store.replaceReducer(createReducer(nextRootReducer))
}, )
}
return store
}
reducers 文件下的 index.js, combineReducers各个模块的 reducer
import { combineReducers } from 'redux';import { latestNews } from './latestNewsReducer';
import { special } from "./specialReducer";
import { themes } from "./themesReducer";
export default createReducer = (injectedReducers) => {
return combineReducers({
latestNews: latestNews,
special: special,
themes: themes,
router: router,
...injectedReducers
})
}
接下来就是 各个模块的 reducer了,接受action 返回的 state 或者data,由于都是get请求,各个模块的请求都大同小异,以最新模块为例, latestNewsReducer.js 如下
import { RQUEST_LATESTNEWS, SUC_LATESTNEWS, DESTORY_LATESTNEWS } from '../actions/latestnews/types';export const latestNews = (state = null, action) => {
switch (action.type) {
case RQUEST_LATESTNEWS:
return state
case SUC_LATESTNEWS:
return Object.assign({}, state, {
data: action.data
})
case DESTORY_LATESTNEWS:
return null
default:
return state;
}
}
type 为常理单独写出来的 理应 单独新建 const 目录用于放各个模块的 type,图快就都挨着action放了
还是 以 最新模块 为例子 type.js
//进入请求请求 export const FETCHED_LATESTNEWS = 'fetched_latestnews'
//发送请求
export const RQUEST_LATESTNEWS = 'request_latestnews'
//请求成功 返回数据
export const SUC_LATESTNEWS = 'suc_latestnews'
//销毁
export const DESTORY_LATESTNEWS = 'destory_latestnews' //当离开当前页面时 返回此 置空stroe对应的值
latestNews的action 也很简单
import { RQUEST_LATESTNEWS, SUC_LATESTNEWS, FETCHED_LATESTNEWS, DESTORY_LATESTNEWS } from './types';//初始请求
export const fetchedLatestNews = () => {
return {
type: FETCHED_LATESTNEWS
}
}
//开始发送请求
export const requestLatestNews = () => {
return {
type: RQUEST_LATESTNEWS
}
}
//请求成功
export const sucLatestNews = (data) => {
return {
type: SUC_LATESTNEWS,
data
}
}
//销毁
export const destoryLatestnews = () => {
return {
type: DESTORY_LATESTNEWS
}
}
现在开始sagas的编写
-------------------------------------------------嗯··---------------------------------------------
-------------------------------------------------辣眼睛---------------------------------------------
sagas目录下index 统一引入各个模块 对应的 请求方法
index.js
import { all, takeEvery, fork } from 'redux-saga/effects';import { FETCHED_LATESTNEWS } from '../actions/latestnews/types'
import { getLatestNews } from './latestnews';
import { FETCHED_SPECICAL } from '../actions/special/types';
import { getSpecial } from './special';
import { FETCHED_THEMES } from '../actions/themes/types';
import { getThemes } from './themes';
export default function* rootSaga() {
yield takeEvery(FETCHED_LATESTNEWS, getLatestNews);
yield takeEvery(FETCHED_THEMES, getThemes);
yield takeEvery(FETCHED_SPECICAL, getSpecial);
}
还是以最新为例:
import { put, call } from 'redux-saga/effects';import { SUC_LATESTNEWS } from '../actions/latestnews/types';
import { repoLoadingError, requestLatestNews, sucLatestNews } from "../actions/latestnews/index";
import { GetLatestNews } from '../apis/latestnews/fetch';
export function* getLatestNews() {
try {
yield put(requestLatestNews())
const data = yield call(GetLatestNews);
yield put({type: SUC_LATESTNEWS, data});
} catch (error) {
yield put(repoLoadingError(error));
}
}
现在到容器了
container, 对应的 最新模块的container
latestnews.js 是嵌套在第个 tabNavigator 里的 ,和热门平级, tabNavigator 配置都大同小异 此处省略···
import React, { Component } from 'react';import { View, Text } from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { requestLatestNews, sucLatestNews, fetchedLatestNews } from '../../actions/latestnews/index';
class LatestNews extends Component {
componentDidMount() {
this.props.fetchedLatestNews()
}
render() {
let list = this.props.latestNews;
return (
<View>
{list ? <Text>{JSON.stringify(this.props.latestNews)}
</Text> : <Text>LOADING</Text>
}
</View>
)
}
}
const mapStateToProps = (state) => {
return {
latestNews: state.latestNews
}
}
const matchDispatchToProps = (dispatch) => {
return bindActionCreators({
fetchedLatestNews: fetchedLatestNews,
}, dispatch)
}
export default connect(mapStateToProps, matchDispatchToProps)(LatestNews);
进行到这里,配置react-navigation,
tabNavigator.js
import React, { Component } from 'react';import { View, Image, StyleSheet } from "react-native";
import { TabNavigator } from "react-navigation";
import News from '../containers/latestnews';
import Themes from '../containers/themes';
import Special from '../containers/special';
export default tabNavigator = TabNavigator(
{
newsTab: {
screen: News,
navigationOptions: {
tabBarLabel: '最新',
tabBarIcon: ({ tintColor, focused }) => (
<Image resizeMode='contain'
source={require('../icon/icon_latestnews.png')}
style={[style.footImage, {tintColor: tintColor}]}
/>
)
}
},
Themes: {
screen: Themes,
navigationOptions: {
tabBarLabel: '主题',
tabBarIcon: ({ tintColor, focused }) => (
<Image resizeMode='contain'
source={require('../icon/icon_themes.png')}
style={[style.footImage, {tintColor: tintColor}]}
/>
)
}
},
Special: {
screen: Special,
navigationOptions: {
tabBarLabel: '专栏',
tabBarIcon: ({ tintColor, focused }) => (
<Image resizeMode='contain'
source={require('../icon/icon_special.png')}
style={[style.footImage, {tintColor: tintColor}]}
/>
)
}
},
}, {
backBehavior: 'none',
tabBarPosition: 'bottom',
lazy: true,
lazyLoad: true,
initialRouteName: 'newsTab',
tabBarOptions: {
showIcon: true,
pressOpacity: 0.8,
style: {
height: 45,
backgroundColor: '#ffffff',
zIndex: 0,
position: 'relative'
},
labelStyle: {
fontSize: 12,
paddingVertical: 0,
marginTop: 0
},
iconStyle: {
marginTop: -5
},
tabStyle: {
backgroundColor: '#eeeeee',
},
inactiveTintColor: '#212121',
activeTintColor: '#0084ff'
}
}
)
let style = StyleSheet.create({
footImage: {
width: 24,
height: 24
},
});
三个大模块,最新,主题和专栏都放在 一个tabNavigator里,再配置到StackNavigator,最新里还嵌套一个 tabNavigator, 也可以把三个主题都放 stackNavigator里。放在stackNavigator,某些方面会更简单也更直观
import React, { Component } from 'react';import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { addNavigationHelpers, StackNavigator } from 'react-navigation';
import tabNavigator from './tabNavigator';
export const Navigator = StackNavigator({
tab: { screen: tabNavigator }
},{
navigationOptions: {
header: null,
headerBackTitle: null,
headerTintColor: '#333333',
showIcon: false,
swipeEnabled: false,
animationEnabled: false,
initialRouteName: 'tab',
lazy: true,
},
mode: 'card',
lazy: true,
});
export default Navigator;
当页面滑动离开当前模块 进入下一个模块 ,减小开支,可以选择把离开模块的store 清空,修改入口文件如下
调用onNavigationStateChange 方法
class Index extends Component { _onNavigationStateChange = (prevState, nextState) => {
let prev = prevState.routes[0],
preIndex = prev.index,
preRouteName = prev.routes[preIndex].key,
switch (preRouteName) {
case 'newsTab':
store.dispatch(destoryLatestnews())
break;
case 'Themes':
store.dispatch(destoryThemes())
break;
default:
store.dispatch(destorySpecial())
break;
}
}
render() {
return (
<Provider store={store}>
<AppStackNavigator onNavigationStateChange={(prevState, currentState) => {
this._onNavigationStateChange(prevState, currentState)
}}/>
</Provider>
);
}
}
export default Index;
本来想在页面离开,组件销毁时 在componentWillUnmount 里调用 destoryLatesNews() 这些方法的,但是滑动切换的时候组件并不会销毁,那么只有更新周期函入手了。
---------------------------------------------------------------------
ps: 知道怎么在componentWillUnmount 里调用 destoryLatesNews 方法或者 进入到此函数的敬请指点,求告知呀
---------------------------------------------------------------------
那么就需要修改一下 latestnews 进入到在更新周期函数中如下写入
shouldComponentUpdate(nextProps, nextState) { if (nextProps.navigation.state.routeName === 'newsTab' && this.props.latestNews == null) {
this.props.fetchedLatestNews()
return true;
}
return false;
}
大体流程就这样了,当然redux-saga 各个函数方法的用途还需多理解,为什么 没进入 componentWillUnmount
是为什么呢·····哈哈哈哈· 等告知·····
有需要的交流的可以加个好友
简单的说搞react开发的痛点之一,单向数据流的传递,redux统一管理数据,redux-saga又处理管理了异步调用。
要实现的内容如下,界面
目录结构
首先从请求接口入手,用axios封装请求接口,统一处理请求 axios.js
import axios from 'axios'let defaultConfig = {
timeout: 3000,
}
let instance = axios
class Axios {
constructor(props) {
if (props && typeof props == 'object') {
instance = axios.create(props)
} else {
instance = axios.create(defaultConfig);
}
//拦截
instance.interceptors.request.use((config) => {
return config;
}, (error) => {
console.log(error)
return Promise.reject(error);
});
//日志 响应结果
instance.interceptors.response.use((response) => {
return response.data;
}, (error) => {
console.log(error)
return Promise.reject(error);
})
}
send(params) {
if (!params || typeof params != 'object') {
throw new Error('params is undefined or not an object')
}
if (params.method == 'GET') {
return get(params.url)
} else if (params.method == 'POST') {
return post(params.url, params)
}
}
}
async function get(url, callback) {
try {
let response = await instance.get(url)
return response
} catch (e) {
console.log(e)
}
}
async function post(url, params, callback) {
try {
let response = await instance.post(url)
//eturn callback(response)
return response
} catch (e) {
console.log(e)
}
}
export default Instance = new Axios();
store.js 管理以及开发环境下的及时更新const sagamiddleware = createSagaMiddleware();export default function configureStore(initStore = {}) {
const middlewares = [sagamiddleware];
if (__DEV__) {
middlewares.push(logger)
}
const createStoreMiddleware = applyMiddleware(...middlewares)(createStore);
const store = createStoreMiddleware(
createReducer(), initStore
);
store.runSaga = sagamiddleware.run;
store.close = () => store.dispatch(END);
// Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
module.hot.accept(() => {
const nextRootReducer = require('../reducers/index').default; //reducers 文件下的 index
store.replaceReducer(createReducer(nextRootReducer))
}, )
}
return store
}
reducers 文件下的 index.js, combineReducers各个模块的 reducer
import { combineReducers } from 'redux';import { latestNews } from './latestNewsReducer';
import { special } from "./specialReducer";
import { themes } from "./themesReducer";
export default createReducer = (injectedReducers) => {
return combineReducers({
latestNews: latestNews,
special: special,
themes: themes,
router: router,
...injectedReducers
})
}
接下来就是 各个模块的 reducer了,接受action 返回的 state 或者data,由于都是get请求,各个模块的请求都大同小异,以最新模块为例, latestNewsReducer.js 如下
import { RQUEST_LATESTNEWS, SUC_LATESTNEWS, DESTORY_LATESTNEWS } from '../actions/latestnews/types';export const latestNews = (state = null, action) => {
switch (action.type) {
case RQUEST_LATESTNEWS:
return state
case SUC_LATESTNEWS:
return Object.assign({}, state, {
data: action.data
})
case DESTORY_LATESTNEWS:
return null
default:
return state;
}
}
type 为常理单独写出来的 理应 单独新建 const 目录用于放各个模块的 type,图快就都挨着action放了
还是 以 最新模块 为例子 type.js
//进入请求请求 export const FETCHED_LATESTNEWS = 'fetched_latestnews'
//发送请求
export const RQUEST_LATESTNEWS = 'request_latestnews'
//请求成功 返回数据
export const SUC_LATESTNEWS = 'suc_latestnews'
//销毁
export const DESTORY_LATESTNEWS = 'destory_latestnews' //当离开当前页面时 返回此 置空stroe对应的值
latestNews的action 也很简单
import { RQUEST_LATESTNEWS, SUC_LATESTNEWS, FETCHED_LATESTNEWS, DESTORY_LATESTNEWS } from './types';//初始请求
export const fetchedLatestNews = () => {
return {
type: FETCHED_LATESTNEWS
}
}
//开始发送请求
export const requestLatestNews = () => {
return {
type: RQUEST_LATESTNEWS
}
}
//请求成功
export const sucLatestNews = (data) => {
return {
type: SUC_LATESTNEWS,
data
}
}
//销毁
export const destoryLatestnews = () => {
return {
type: DESTORY_LATESTNEWS
}
}
现在开始sagas的编写
-------------------------------------------------嗯··---------------------------------------------
-------------------------------------------------辣眼睛---------------------------------------------
sagas目录下index 统一引入各个模块 对应的 请求方法
index.js
import { all, takeEvery, fork } from 'redux-saga/effects';import { FETCHED_LATESTNEWS } from '../actions/latestnews/types'
import { getLatestNews } from './latestnews';
import { FETCHED_SPECICAL } from '../actions/special/types';
import { getSpecial } from './special';
import { FETCHED_THEMES } from '../actions/themes/types';
import { getThemes } from './themes';
export default function* rootSaga() {
yield takeEvery(FETCHED_LATESTNEWS, getLatestNews);
yield takeEvery(FETCHED_THEMES, getThemes);
yield takeEvery(FETCHED_SPECICAL, getSpecial);
}
还是以最新为例:
import { put, call } from 'redux-saga/effects';import { SUC_LATESTNEWS } from '../actions/latestnews/types';
import { repoLoadingError, requestLatestNews, sucLatestNews } from "../actions/latestnews/index";
import { GetLatestNews } from '../apis/latestnews/fetch';
export function* getLatestNews() {
try {
yield put(requestLatestNews())
const data = yield call(GetLatestNews);
yield put({type: SUC_LATESTNEWS, data});
} catch (error) {
yield put(repoLoadingError(error));
}
}
现在到容器了
container, 对应的 最新模块的container
latestnews.js 是嵌套在第个 tabNavigator 里的 ,和热门平级, tabNavigator 配置都大同小异 此处省略···
import React, { Component } from 'react';import { View, Text } from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { requestLatestNews, sucLatestNews, fetchedLatestNews } from '../../actions/latestnews/index';
class LatestNews extends Component {
componentDidMount() {
this.props.fetchedLatestNews()
}
render() {
let list = this.props.latestNews;
return (
<View>
{list ? <Text>{JSON.stringify(this.props.latestNews)}
</Text> : <Text>LOADING</Text>
}
</View>
)
}
}
const mapStateToProps = (state) => {
return {
latestNews: state.latestNews
}
}
const matchDispatchToProps = (dispatch) => {
return bindActionCreators({
fetchedLatestNews: fetchedLatestNews,
}, dispatch)
}
export default connect(mapStateToProps, matchDispatchToProps)(LatestNews);
进行到这里,配置react-navigation,
tabNavigator.js
import React, { Component } from 'react';import { View, Image, StyleSheet } from "react-native";
import { TabNavigator } from "react-navigation";
import News from '../containers/latestnews';
import Themes from '../containers/themes';
import Special from '../containers/special';
export default tabNavigator = TabNavigator(
{
newsTab: {
screen: News,
navigationOptions: {
tabBarLabel: '最新',
tabBarIcon: ({ tintColor, focused }) => (
<Image resizeMode='contain'
source={require('../icon/icon_latestnews.png')}
style={[style.footImage, {tintColor: tintColor}]}
/>
)
}
},
Themes: {
screen: Themes,
navigationOptions: {
tabBarLabel: '主题',
tabBarIcon: ({ tintColor, focused }) => (
<Image resizeMode='contain'
source={require('../icon/icon_themes.png')}
style={[style.footImage, {tintColor: tintColor}]}
/>
)
}
},
Special: {
screen: Special,
navigationOptions: {
tabBarLabel: '专栏',
tabBarIcon: ({ tintColor, focused }) => (
<Image resizeMode='contain'
source={require('../icon/icon_special.png')}
style={[style.footImage, {tintColor: tintColor}]}
/>
)
}
},
}, {
backBehavior: 'none',
tabBarPosition: 'bottom',
lazy: true,
lazyLoad: true,
initialRouteName: 'newsTab',
tabBarOptions: {
showIcon: true,
pressOpacity: 0.8,
style: {
height: 45,
backgroundColor: '#ffffff',
zIndex: 0,
position: 'relative'
},
labelStyle: {
fontSize: 12,
paddingVertical: 0,
marginTop: 0
},
iconStyle: {
marginTop: -5
},
tabStyle: {
backgroundColor: '#eeeeee',
},
inactiveTintColor: '#212121',
activeTintColor: '#0084ff'
}
}
)
let style = StyleSheet.create({
footImage: {
width: 24,
height: 24
},
});
三个大模块,最新,主题和专栏都放在 一个tabNavigator里,再配置到StackNavigator,最新里还嵌套一个 tabNavigator, 也可以把三个主题都放 stackNavigator里。放在stackNavigator,某些方面会更简单也更直观
import React, { Component } from 'react';import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { addNavigationHelpers, StackNavigator } from 'react-navigation';
import tabNavigator from './tabNavigator';
export const Navigator = StackNavigator({
tab: { screen: tabNavigator }
},{
navigationOptions: {
header: null,
headerBackTitle: null,
headerTintColor: '#333333',
showIcon: false,
swipeEnabled: false,
animationEnabled: false,
initialRouteName: 'tab',
lazy: true,
},
mode: 'card',
lazy: true,
});
export default Navigator;
当页面滑动离开当前模块 进入下一个模块 ,减小开支,可以选择把离开模块的store 清空,修改入口文件如下
调用onNavigationStateChange 方法
class Index extends Component { _onNavigationStateChange = (prevState, nextState) => {
let prev = prevState.routes[0],
preIndex = prev.index,
preRouteName = prev.routes[preIndex].key,
switch (preRouteName) {
case 'newsTab':
store.dispatch(destoryLatestnews())
break;
case 'Themes':
store.dispatch(destoryThemes())
break;
default:
store.dispatch(destorySpecial())
break;
}
}
render() {
return (
<Provider store={store}>
<AppStackNavigator onNavigationStateChange={(prevState, currentState) => {
this._onNavigationStateChange(prevState, currentState)
}}/>
</Provider>
);
}
}
export default Index;
本来想在页面离开,组件销毁时 在componentWillUnmount 里调用 destoryLatesNews() 这些方法的,但是滑动切换的时候组件并不会销毁,那么只有更新周期函入手了。
---------------------------------------------------------------------
ps: 知道怎么在componentWillUnmount 里调用 destoryLatesNews 方法或者 进入到此函数的敬请指点,求告知呀
---------------------------------------------------------------------
那么就需要修改一下 latestnews 进入到在更新周期函数中如下写入
shouldComponentUpdate(nextProps, nextState) { if (nextProps.navigation.state.routeName === 'newsTab' && this.props.latestNews == null) {
this.props.fetchedLatestNews()
return true;
}
return false;
}
大体流程就这样了,当然redux-saga 各个函数方法的用途还需多理解,为什么 没进入 componentWillUnmount
是为什么呢·····哈哈哈哈· 等告知·····
有需要的交流的可以加个好友
以上是 react-native,redux,redux-saga组合开发实现 的全部内容, 来源链接: utcz.com/z/383582.html