【水滴石穿】RNNewsGo
阅读原文时间:2023年07月10日阅读:2

项目地址为:https://github.com/ImVeryGood/RNNewsGo

我们先来看一下页面

分析了一下项目,项目也是有用到redux-saga的

然后模块比较清晰

接下来我们分析代码

//index.js
/**
 * @format
 */

import { AppRegistry } from "react-native";
import App from "./App";
import { name as appName } from "./app.json";

AppRegistry.registerComponent(appName, () => App);

根index.js引用的是根App.js

固定的可切换底部

//src/screens/base/Navigations.js
import {
  createAppContainer,
  createBottomTabNavigator,
  createStackNavigator
} from "react-navigation";
import { Image, StyleSheet } from "react-native";
import News from "../news/News";
import React from "react";
import Mine from "../mine/Mine";
import Picture from "../picture/Picture";
import Video from "../video/Video";
import TopNavigation from "../picture/components/TopNavigation";
const tabBottom = createBottomTabNavigator(
  {
    News: {
      screen: News,
      navigationOptions: {
        header: null,
        tabBarLabel: "新闻",
        tabBarIcon: ({ focused }) => {
          return (
            <Image
              style={styles.tabBarIcon}
              source={
                focused
                  ? require("./image/b_newhome_tabbar_press.png")
                  : require("./image/b_newhome_tabbar.png")
              }
            />
          );
        }
      }
    },
    Picture: {
      screen: Picture,
      navigationOptions: {
        header: null,
        tabBarLabel: "图片",
        tabBarIcon: ({ focused }) => {
          return (
            <Image
              style={styles.tabBarIcon}
              source={
                focused
                  ? require("./image/b_newcare_tabbar_press.png")
                  : require("./image/b_newcare_tabbar.png")
              }
            />
          );
        }
      }
    },
    Video: {
      screen: Video,
      navigationOptions: {
        header: null,
        tabBarLabel: "视频",
        tabBarIcon: ({ focused }) => {
          return (
            <Image
              style={styles.tabBarIcon}
              source={
                focused
                  ? require("./image/b_newdiscover_tabbar_press.png")
                  : require("./image/b_newdiscover_tabbar.png")
              }
            />
          );
        }
      }
    },
    Mine: {
      screen: Mine,
      navigationOptions: {
        header: null,
        tabBarLabel: "我的",
        tabBarIcon: ({ focused }) => {
          return (
            <Image
              style={styles.tabBarIcon}
              source={
                focused
                  ? require("./image/b_newmine_tabbar_press.png")
                  : require("./image/b_newmine_tabbar.png")
              }
            />
          );
        }
      }
    }
  },
  {
    initialRouteName: "News",
    swipeEnabled: true,
    indicatorStyle: { height: 0 },
    tabBarOptions: {
      //当前选中的tab bar的文本颜色和图标颜色
      activeTintColor: "red",
      //当前未选中的tab bar的文本颜色和图标颜色
      inactiveTintColor: "#000"
    }
  }
);
const App = createStackNavigator({
  Main: {
    screen: tabBottom,
    navigationOptions: {
      header: null,
      indicatorStyle: { height: 0 }
    }
  }
});

export default createAppContainer(App);
const styles = StyleSheet.create({
  tabBarIcon: {
    width: 30,
    height: 30
  }
});


//App.js
import React, { Component } from "react";
//固定的底部切换
import Navigations from "./src/screens/base/Navigations";
import { Provider } from "react-redux";
//有用到数据持久化
import { store, persistor } from "./src/store/configStore";
import { PersistGate } from "redux-persist/integration/react";
import { Router } from "react-native-router-flux";
import scenes from "./src/scenes";
type Props = {};
export default class App extends Component<Props> {
  onBeforeLift = () => {
    // take some action before the gate lifts
  };
  render() {
    return (
      <Provider store={store}>
        <PersistGate persistor={persistor} loading={this.onBeforeLift()}>
          <Navigations />
        </PersistGate>
      </Provider>
    );
  }
}

接下来是切换时对应的每一个tab的页面

新闻页面

//src/screens/news/News.js
//newsaction里面没有什么内容
import { actionTypes } from "./actionTypes";

const getNewsData = () => {
  return {
    type: actionTypes.NEWS
  };
};
export const news_action = {
  getNewsData
};


//src/saga/news_saga.js
import {
  put,
  call,
  takeLatest,
  takeEvery,
  fork,
  take,
  select
} from "redux-saga/effects";
import "whatwg-fetch";
import { actionTypes } from "../action/actionTypes";
import fetchSmart from "../service/fetch";
import { urlMap } from "../service/UrlMap";
function* getNews() {
  while (true) {
    yield take(actionTypes.NEWS);
    // fetchSmart(urlMap.bannerUrl)
    //   .then(response => {
    //     alert(response);
    //   })
    //   .catch(error => {
    //     alert(error);
    //   });
    fetch(urlMap.news_url, {
      method: "GET",
      headers: { Connection: "true" }
    })
      .then(response => response.json())
      .then(data => console.log("AAAAAdata is", data))
      .catch(error => console.log("AAAAAerror is", error));
    yield put({
      type: actionTypes.NEWS_SAGA,
      news_saga: Math.random(100 - 1)
    });
  }
}
export default function* root(): any {
  yield fork(getNews);
}

点击进入图片

也是对里面的内容进行了模块的封装

//src/screens/picture/Picture.jsimport React from "react";
import { View, Button, Text, Image, StyleSheet, FlatList } from "react-native";
import { bindActionCreators } from "redux";
import index_action from "../../action/index_action";
import { connect } from "react-redux";
import TobNavigation from "./components/TopNavigation";

class Picture extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  _navigationStateChange = index => {
    this.props.actions.getRecommend(index);
  };
  render() {
    return (
      <TobNavigation
        onNavigationStateChange={(prevState, currentState) => {
          this._navigationStateChange(currentState.index);
        }}
      />
    );
  }
}

const styles = StyleSheet.create({});
const mapStateToProps = state => {
  return {};
};
const mapDispatchToProps = dispatch => {
  return {
    actions: bindActionCreators(index_action, dispatch)
  };
};
module.exports = connect(
  mapStateToProps,
  mapDispatchToProps
)(Picture);

上面的是在图片目录下可以切换的内容

每一个screen对应的也是单独的页面

//src/screens/picture/components/TopNavigation.js
import { ColorSource } from "../../../sources/ColorSource";
import Recommend from "./Recommend";
import StarPic from "./StarPic";
import NewsPic from "./NewsPic";
import {
  createMaterialTopTabNavigator,
  createAppContainer
} from "react-navigation";
/**
 * createMaterialTopTabNavigator,实现顶部Tab栏的切换
 */
const TobBar = createMaterialTopTabNavigator(
  {
    Recommend: {
      screen: Recommend,
      navigationOptions: {
        tabBarLabel: "推荐"
      }
    },
    NewsPic: {
      screen: NewsPic,
      navigationOptions: {
        tabBarLabel: "新闻"
      }
    },
    StarPic: {
      screen: StarPic,
      navigationOptions: {
        tabBarLabel: "明星"
      }
    }
  },
  {
    tabBarOptions: {
      //tab bar的位置, 可选值: 'top' or 'bottom'
      tabBarPosition: "top",
      swipeEnabled: true,
      allowFontScaling: true,
      scrollEnabled: false,
      style: {
        backgroundColor: ColorSource.blue //设置顶部tab的背景颜色
      },
      //  设置下划线的颜色
      indicatorStyle: {
        backgroundColor: ColorSource.red
      },
      activeTintColor: ColorSource.red, //设置选中字的颜色
      inactiveTintColor: ColorSource.white //设置未选中的颜色样式
    },
    // style: { backgroundColor: ColorSource.red },
    initialRouteName: "Recommend"
  }
);

export default createAppContainer(TobBar);

这个是recommend组件

//src/screens/picture/components/Recommend.js
//里面也还是有封装
import {
 View,
 Text,
 FlatList,
 Button,
 Modal,
 Image,
 StyleSheet,
 TouchableWithoutFeedback
} from "react-native";
import React from "react";
import { bindActionCreators } from "redux";
import index_action from "../../../action/index_action";
import { connect } from "react-redux";
import PictureListComponent from "./PictureListComponent";
class Recommend extends React.Component {
 constructor(props) {
   super(props);
   this.state = {
     recommend: [],
     isShowModule: false,
     pic: []
   };
 }
 componentDidMount() {
   this.props.actions.getRecommend(0);
 }
 render() {
   const { recommend } = this.props;

   return (
     <View>
       <PictureListComponent data={recommend} />
     </View>
   );
 }
}
const styles = StyleSheet.create({});
const mapStateToProps = state => {
 return { recommend: state.picture_reducer.get("recommend") };
};
const mapDispatchToProps = dispatch => {
 return {
   actions: bindActionCreators(index_action, dispatch)
 };
};
module.exports = connect(
 mapStateToProps,
 mapDispatchToProps
)(Recommend);

接下来进行的就是图片内的组件

这个只是封装的单个的图片组件

//src/screens/picture/components/PictureListComponent.js
import React from "react";
import {
  View,
  VirtualizedList,
  StyleSheet,
  ScrollView,
  RefreshControl,
  Text,
  Platform,
  FlatList,
  TouchableWithoutFeedback,
  Image,
  Modal
} from "react-native";
import PictureModule from "./PictureModule";
import { ColorSource } from "../../../sources/ColorSource";
type Props = {
  data?: Array<any>
};
class PictureListComponent extends React.Component {
  constructor(props: Props) {
    super(props);
    this.state = {
      isShowModule: false,
      pic: []
    };
  }
  _showPic(item, index) {
    this.setState({
      isShowModule: !this.state.isShowModule,
      pic: item.pics
    });
  }
  _emptyView = () => {
    return (
      <View style={styles.emptyView}>
        <Text>暂无数据</Text>
      </View>
    );
  };
  _renderItem = ({ item, index }) => {
    return (
      <TouchableWithoutFeedback onPress={() => this._showPic(item, index)}>
        <View style={styles.item}>
          <Image
            source={{
              uri: item.scover
            }}
            style={styles.image}
          />
          <Text style={styles.name}>{item.setname}</Text>
        </View>
      </TouchableWithoutFeedback>
    );
  };
  _ModalPic() {
    if (this.state.pic.length > 0) {
      return (
        <Modal
          onRequestClose={() => {
            this.setState({
              isShowModule: false
            });
          }}
          visible={this.state.isShowModule}
        >
          <PictureModule pic={this.state.pic} />
        </Modal>
      );
    }
  }
  render() {
    return (
      <View>
        <FlatList
          renderItem={this._renderItem}
          data={this.props.data}
          extraData={this.state}
          keyExtractor={(item, index) => index.toString()}
          numColumns={1}
          ListEmptyComponent={this._emptyView()}
          disableVirtualization={true}
        />
        {this._ModalPic()}
      </View>
    );
  }
}
const styles = StyleSheet.create({
  emptyView: {
    flex: 1,
    alignItems: "center"
  },
  item: {
    height: 180,
    marginBottom: 10
  },
  name: {
    position: "absolute",
    marginLeft: "5%",
    marginRight: "5%",
    height: 50,
    lineHeight: 50,
    bottom: 0,
    width: "90%",
    backgroundColor: ColorSource.grey
  },
  image: {
    height: 180,
    width: "90%",
    marginLeft: "5%",
    marginRight: "5%"
  }
});
module.exports = PictureListComponent;


//src/screens/picture/components/PictureModule.js
//这个里面的图片是可以点击滑动的意思
import React from "react";
import { View, Image, Text, StyleSheet } from "react-native";
import Swiper from "react-native-swiper";
class PictureModule extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  _renderSwiper() {
    let itemArr = [];
    let dataArr = this.props.pic;
    if (dataArr.length > 0) {
      for (let i = 0; i < dataArr.length; i++) {
        let pic = {
          uri: dataArr[i]
        };
        itemArr.push(<Image source={pic} style={styles.image} />);
      }
    }
    return itemArr;
  }
  render() {
    return (
      <Swiper showsPagination={true} showsButtons={true}>
        {this._renderSwiper()}
      </Swiper>
    );
  }
}
const styles = StyleSheet.create({
  image: {
    flex: 1
  }
});
module.exports = PictureModule;


//src/screens/picture/components/StarPic.js
//这个都不知道是做什么的~
import { View, Text } from "react-native";
import React from "react";
import { bindActionCreators } from "redux";
import index_action from "../../../action/index_action";
import { connect } from "react-redux";
import PictureListComponent from "./PictureListComponent";

class StarPic extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const { star } = this.props;
    return (
      <View>
        <PictureListComponent data={star} />
      </View>
    );
  }
}

const mapStateToProps = state => {
  return { star: state.picture_reducer.get("star") };
};
const mapDispatchToProps = dispatch => {
  return {
    actions: bindActionCreators(index_action, dispatch)
  };
};
module.exports = connect(
  mapStateToProps,
  mapDispatchToProps
)(StarPic);

//视频页面什么都没有
//src/screens/video/Video.js
import React from "react";
import { View, Button, Text, Image, StyleSheet } from "react-native";
import { bindActionCreators } from "redux";
import index_action from "../../action/index_action";
import { connect } from "react-redux";

class Video extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return <View />;
  }
}

const styles = StyleSheet.create({});
const mapStateToProps = state => {
  return {};
};
const mapDispatchToProps = dispatch => {
  return {
    actions: bindActionCreators(index_action, dispatch)
  };
};
module.exports = connect(
  mapStateToProps,
  mapDispatchToProps
)(Video);

关于我的页面

//src/screens/mine/Mine.js
import React from "react";
import { View, Button, Text, Image, StyleSheet, FlatList } from "react-native";
import { bindActionCreators } from "redux";
import index_action from "../../action/index_action";
import { connect } from "react-redux";

export class Mine extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return <View />;
  }
}

const styles = StyleSheet.create({});
const mapStateToProps = state => {
  return {};
};
const matchDispactchToProps = dispatch => {
  return {
    actions: bindActionCreators(index_action, dispatch)
  };
};
module.exports = connect(
  mapStateToProps,
  matchDispactchToProps
)(Mine);

看了半天原来数据是在redux-saga里面进行处理的啊

//src/saga/picture_saga.js
import {
  put,
  call,
  takeLatest,
  takeEvery,
  fork,
  take,
  select
} from "redux-saga/effects";
import "whatwg-fetch";
import { actionTypes } from "../action/actionTypes";
import fetchSmart from "../service/fetch";
import { urlMap, pic_url } from "../service/UrlMap";

function* getRecommend() {
  while (true) {
    const action = yield take(actionTypes.RECOMMEND);
    const index = action.index;
    const url = pic_url[index];
    try {
      const res = yield call(fetchSmart, url);
      if (index === 0) {
        yield put({ type: actionTypes.RECOMMEND_SAGA, recommend: res });
      } else if (index === 1) {
        yield put({ type: actionTypes.NEWS_SAGA, news: res });
      } else if (index === 2) {
        yield put({ type: actionTypes.STAR_SAGA, star: res });
      }
    } catch (e) {
      alert(e);
    }
  }
}
export default function* root(): any {
  yield fork(getRecommend);
}