使用 react-navigation + axios + redux新建react-native项目

新建react native项目

  • 搭建开发环境
  • 使用React Navigation实现页面跳转
  • 使用axios网络库
  • 使用redux管理数据状态
    • 基本概念:
    • 其他函数
    • 使用connect装饰器
    • redux工作流程
    • 相关代码:

搭建开发环境

参考react native 官网搭建开发环境:
https://reactnative.cn/docs/getting-started.html
新建项目:

react-native init RNApp

然后打开 ios/RNApp.xcodeproj ,在xcode中运行项目。
编译失败:

解决办法:
cd RNApp/node_modules/react-native/third-party/glog-0.3.4
然后执行 ./configure, 如下所示:
使用 react-navigation + axios + redux新建react-native项目_第1张图片
然后再次运行,又遇到另一个问题
使用 react-navigation + axios + redux新建react-native项目_第2张图片
解决办法:
找到这个文件,删除再添加。
使用 react-navigation + axios + redux新建react-native项目_第3张图片
重新编译运行就可以了。app的入口是index.js文件:

import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('RNApp', () => App);

在index.js中,将App注册为项目的根组件,运行项目后首先看到的是App.js渲染出的页面。
使用 react-navigation + axios + redux新建react-native项目_第4张图片
接下来加入路由跳转,网络请求和redux管理数据。
总的文件目录结构如下:
使用 react-navigation + axios + redux新建react-native项目_第5张图片

使用React Navigation实现页面跳转

搭建好一个新项目之后,要使用导航实现页面跳转,可以使用RN官方推荐的react-navigation库。文档链接:https://reactnavigation.org/docs/en/getting-started.html
首先安装navigation包:

yarn add react-navigation

为了项目结构看起来更清楚,在项目的根目录下建一个app文件夹,然后在里面建一个index.js文件作为 APP 的根组件。同时将index中注册的组件改成app下的index:

import { AppRegistry } from 'react-native';
import App from './app/index';

AppRegistry.registerComponent('RNApp', () => App);

在app中新建一个pages文件夹,用来放APP的页面,然后在pages中新建两个页面,分别为MainScreen.jsDetailScreen.js
在MainScreen中放一个button:

  <Button
    onPress={this.gotDetail}
    title="进入详情"
    color="#841584"
  />

点击进入DetailScreen页面,详情中放一个text:

  <Text>
  	DetailScreen
  </Text>

然后在app中新建一个route.js文件,对路由进行配置:

import { createStackNavigator } from 'react-navigation';
import MainSreen from './pages/MainSreen';
import DetailScreen from './pages/DetailScreen';

const RootStack = createStackNavigator(
  {
    Home: MainSreen,
    Details: DetailScreen,
  },
  {
    initialRouteName: 'Home',
  }
);

export default RootStack;

在route.js中使用createStackNavigator创建了一个路由栈,在路由栈配置了Home和Details两个路由,初始路由是Home。
在app/index.js中调用RootStack组件,

import React, { Component } from 'react';
import { View } from 'react-native';
import RootStack from './router';

export default class App extends Component {
  render() {
    return (
      <View style={{ flex: 1 }}>
        <RootStack />
      </View>
    );
  }
}

然后在MainScreen.js中添加点击进入详情的事件:

  gotoDetail = (id) => {
    this.props.navigation.push('Details', {
      text: 'Text from MainScreen...',
    });
  }

navigation的props会传递给在route中定义了的路由,所以这里可以调用navigation.push()往栈中添加一个新页面,同时传了一个text参数过去。
在DetailScreen中从navigation的state中可以获取到从上个页面传下来的参数,所有参数都在params中。在detail中将获取到的text展示出来,代码如下:

   render() {
    const { text } = this.props.navigation.state.params;
    return (
      <View style={styles.container}>
        <Text style={styles.content}>
          DetailScreen
        </Text>
        <Text style={styles.content}>
          {text}
        </Text>
      </View>
    );
   }

点击进入详情结果如下,页面中展示了从首页传过来的text:

使用 react-navigation + axios + redux新建react-native项目_第6张图片

使用axios网络库

首先安装axios:

yarn add axios

安装好之后就可以直接用了。
请求方法有:
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
使用axios.all(reqestArr) 可以同时发多个请求。
使用之前可以做一些统一的配置,打印请求接口和返回值,在app中新建setAxios.js :

import axios from 'axios';

const token = 'token';
const headers = {
    Accept: 'application/json;charset=utf-8',
    'Content-Type': 'application/json;charset=utf-8',
    'Accept-Encoding': 'gzip, deflate',
};

export function setAxios() {
    axios.defaults.headers = headers;
    axios.defaults.headers['x-auth-token'] = token;
    axios.defaults.baseURL = '127.0.0.1:8080';
    // 添加请求拦截器
    axios.interceptors.response.use((response) => {
        const { config, data, request} = response;
        console.log('请求的url: ', `${config.method}:${request.responseURL}`);
        console.log('token: ', config.headers['x-auth-token']);
        console.log('请求body: ', config.data ? JSON.parse(config.data) : undefined);
        console.log('返回值: ', data);
        if (data.code === 0) {
            return Promise.resolve(data);
        }
        return Promise.reject(data);
    })
}

可以在app/index.js中调用setAxios();

  componentDidMount() {
    setAxios();
  }

接口请求:

axios.get('url').then((res) => {
//请求成功处理
}).catch((e) => {
//错误处理
})

请求接口时会打印出日志信息。

使用redux管理数据状态

Redux 是一个 JavaScript 的状态管理库。使用前要装redux,react-redux,redux-devtools开发者工具和redux-thunk中间件:

yarn add redux
yarn add react-redux
yarn add redux-devtools
yarn add redux-thunk

基本概念:

  1. store:store是保存数据的地方,应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。
  2. state:state是store中的数据在某个时间点的状态。store提供了 getState() 方法获取当前 state,提供了 dispatch(action) 方法更新 state。
  3. action:action是用来描述当前发生了什么的一个对象,将数据从view传到store中。通过store.dispatch()来发起一个action。
  4. reducer:reducer 是一个纯函数,接收旧的 state 和 action,描述怎么更新state,返回新的 state。

其他函数

  1. createStore(reducer, [preloadedState], enhancer): 用来生成一个store, createStore函数接受另一个函数作为参数,返回新生成的 Store 对象。第一个参数是reducer,第二个参数是初始的 state,第三个参数是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。
  2. subscribe:设置监听函数,一旦 state 发生变化,就自动执行这个函数。
  3. compose(…funcs) : 从右到左组合多个函数。compose(f1,f2,f3(args)) = f1(f2(f3(args))))。
  4. applyMiddleware:作用是将所有中间件组成一个数组,依次执行。

使用connect装饰器

connect装饰器的作用是,使该页面可以监听到store的变化。首先安装:

yarn add babel-plugin-transform-decorators-legacy

装好包之后在APP的.babelrc文件中配置:
使用 react-navigation + axios + redux新建react-native项目_第7张图片
然后就可以在页面中调用了。在页面中加入connect装饰器之后,这个页面相关的数据变了就会重新渲染页面组件。调用方式:

@connect(state => ({
  detailData: state.detailData,
}))

connect的原理详解:https://www.jianshu.com/p/9873d4ccb891

redux工作流程

1、创建store并初始化:

const logger = createLogger();
const store = createStore(
    rootReducer,
    initialStore,
    applyMiddleware(thunk, logger),
);

其中第二个和第三个参数不是必须的,initialStore用来指定初始的store, applyMiddleware(thunk, logger),用来调用中间件。
创建store之后要在app的根组件中调用:

  render() {
    return (
      <Provider store={store}>
        <RootStack />
      </Provider>
    );
  }

Provider的作用是使组件层级中的 connect() 方法都能够获得 redux的store。
2、使用store.dispatch(action)将action传到store

 store.dispatch({
    type: actionTypes.FETCH_HOME_DATA,
    homeData: res.data,
 });

3、调用dispatch发起一个action之后store会自动调用reducer方法,并将当前 state 和收到的 action传给reducer,返回新的state。reducer中描述了怎么更新state。

const home = (state=initialStore.homeData, action) => {
  switch(action.type) {
    case 'FETCH_HOME_DATA':
      return action.homeData;
    case 'DELETE_HOME_DATA':
      return {}
    default:
      return state;
  }
}

这是其中一个reducer函数,可以通过combineReducers将多个reducer组合起来。

const rootReducer = combineReducers({
  homeData,
  detailData,
})

4、使用store.getState()可以获取到最新的state。在页面中使用了connect()之后可以监听到store的变化,store变了之后 state => ({ detailData: state.detailData }) 就会被调用,重新渲染组件。

相关代码:

在app中新建一个store文件夹用来存放store相关的文件。新建一个store.js文件创建store。app/store/store.js代码:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import rootReducer from '../reducers';

const store = createStore(
    rootReducer,
    applyMiddleware(thunk),
);
export default store;

新建app/store/initialStore.js文件初始化store

const initialStore = {
    homeData: {},
    detailData: {},
}

export default initialStore;

在api中定义接口 app/api/index.js:

import axios from 'axios';

export function getHomeData() {
  return axios.get(`/api/home`);
}

export function getDetailData(id) {
  return axios.get(`/api/${id}`);
}

app/actions/actionTypes.js定义actionType

const actionTypes = {
  FETCH_HOME_DATA: 'FETCH_HOME_DATA',
  DELETE_HOME_DATA: 'DELETE_HOME_DATA',
  FETCH_DETAIL_DATA: 'FETCH_DETAIL_DATA',
  DELETE_DETAIL_DATA: 'DELETE_DETAIL_DATA',
}
export default actionTypes;

app/actions/index.js调用接口请求数据

import { getHomeData, getDetailData } from '../api/index';
import actionTypes from './actionTypes';
import store from '../stores/store';

export function fetchHomeData() {
  getHomeData().then((res) => {
    return store.dispatch({
        type: actionTypes.FETCH_HOME_DATA,
        homeData: res.data,
      });
  }).catch(e => {
    console.log(e);
  })
}

export function fetchDetailData(id) {
  getDetailData(id).then((res) => {
    return store.dispatch({
        type: actionTypes.FETCH_DETAIL_DATA,
        detailData: res.data,
      });
  })
}

首页的reducer,更新store中首页的数据: app/reducers/home.js

import initialStore from '../stores/initialStore';

const home = (state=initialStore.homeData, action) => {
  switch(action.type) {
    case 'FETCH_HOME_DATA':
      return action.homeData;
    case 'DELETE_HOME_DATA':
      return {}
    default:
      return state;
  }
}

export default home;

详情页的reducer:app/reducers/detail.js

import initialStore from '../stores/initialStore';

const detail = (state=initialStore.detailData, action) => {
  switch(action.type) {
    case 'FETCH_DETAIL_DATA':
      return action.detailData;
    case 'DELETE_DETAIL_DATA':
      return {}
    default:
      return state;
  }
}

export default detail;

app/reducer/index.js,调用combineReducers将home和detail的数据整合到一起

import { combineReducers } from 'redux';
import homeData from './home';
import detailData from './detail';

const rootReducer = combineReducers({
  homeData,
  detailData,
})

export default rootReducer;

index中调用redux,app/index.js

import React, { Component } from 'react';
import { Provider } from 'react-redux';
import RootStack from './router';
import { setAxios } from './setAxios';
import store from './stores/store';

export default class App extends Component {

  componentDidMount() {
    setAxios();
  }

  render() {
    return (
      <Provider store={store}>
        <RootStack />
      </Provider>
    );
  }
}

你可能感兴趣的:(react,native)