简单的说搞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);
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; }
是为什么呢·····哈哈哈哈· 等告知·····
有需要的交流的可以加个好友