React Native05

此文项目代码:https://github.com/bei-yang/I-want-to-be-an-architect
码字不易,辛苦点个star,感谢!

引言

此篇文章主要涉及以下内容:

  • react navigation 导航框架设计
  • 了解 redux 在 RN 项目(使用 react navigation)中的集成方式
  • Fetch 网络编程

APP导航框架设计

仿主流 APP 设计一个导航框架

欢迎页面设计

import React, { Component } from "React";
import { Platform, StyleSheet, Text, View } from "react-native";

export default class WelcomePage extends Component {
  render() {
    return (
      
        这是欢迎页!
      
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#f8f8f8"
  },
  welcome: {
    fontSize: 20,
    textAlign: "center",
    margin: 10
  }
});

APP 主页设计

import React, { Component } from "react";
import { Platform, StyleSheet, Text, View } from "reacc-native";

export default class HomePage extends Component {
  constructor(props) {
    super(props);
    console.disableYellowBox = true;
  }
  render() {
    return (
      
        这是主页面!
      
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#f8f8f8"
  },
  welcome: {
    fontSize: 20,
    textAlign: "center",
    margin: 10
  }
});

详情页设计

import React, { Component } from 'react'
import { Platform, StyleSheet, Text, View } from 'react-native'

export default class DetailPage extends Component {
  render () {
    return (
      
        这是详情页!
      
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f8f8f8'
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10
  }
})

安装 react navigation 与第三方图标库 react-native-vector-icons

yarn add react-navigation
# or with npm 
# npm install --save react-navigation

yarn add react-native-gesture-handler
# or with npm 
# npm install --save react-native-gesture-handler

react-native link react-native-gesture-handler

yarn add react-native-vector-icons

react-native link react-native-vector-cions

### 记得关闭模拟器,服务器,重新启动项目

设计欢迎页进入主页导航

// AppNavigator.js

import {
  createStackNavigator,
  createAppContainer,
  createSwitchNavigator
} from 'react-navigation'
import HomePage from '../Pages/HomePage'
import WelcomePage from '../Pages/WelcomePage'
import DetailPage from '../Pages/DetailPage'

// 定义欢迎导航
const AppInitNavigator = createStackNavigator({
  WelcomePage: {
    screen: WelcomePage,
    navigationOptions: {
      header: null
    }
  }
});

// 定义主页导航
const AppMainNavigator = createStackNavigator({
  HomePage: {
    screen: HomePage,
    navigationOptions: {
      header: null
    }
  },
  DetailPage: {
    screen: DetailPage
  }
});

export default createAppContainer(
  createSwitchNavigator({
    Init: AppInitNavigator,
    Main: AppMainNavigator
  })
)

APP入口引用导航

import App from './js/Navigator/AppNavigator'

欢迎页面5秒后进入主页

componentDidMount(){
  this.timer = setTimeout(() => {
    const { navigation } = this.props
    navigation.navigate('Main')
  })
}
componentWillUnmount(){
  this.timer && clearTimeout(this.timer)
}

设计一个转场工具类NavigationUtil.js

export default class NavigationUtil {
  // 跳转到指定页面
  static goPage (props, page) {
    const navigation = NavigationUtil.navigation;
    navigation.navigate(page, {
      ...props
    });
  }
  // go back
  static resetGoBack (props) {
    const { navigation } = props;
    navigation.goBack();
  }
  // 回到主页
  static resetToHomePage (params) {
    const { navigation } = params;
    navigation.navigate('Main')
  }
}

欢迎页改造

import NavigationUtil from '../Navigator/navigationUtil'

componentDidMount(){
  this.timer = setTimeout(() => {
    NavigationUtil.resetToHomePage({
      navigation: this.PaymentResponse.navigation
    })
  }, 1000)
}

主页设计底部导航

import React, { Component } from 'react'
import { Platform, StyleSheet, Text, View } from 'react-native'
import {
  createAppContainer,
  createBottomTabNavigator
} from 'react-navigation'
import IndexPage from './IndexPage'
import MyPage from './MyPage'
import VideoPage from './VideoPage'
import FontAwesome from 'react-native-vector-icons/FontAwesome'

const TABS = {
  IndexPage: {
    screen: IndexPage,
    navigationOptions: {
      tabBarLabel: '首页',
      tabBarIcon: ({ tintColor, focused }) => (
        
      )
    }
  },
  VideoPage: {
    screen: VideoPage,
    navigationOptions: {
      tabBarLabel: '视频',
      tabBarIcon: ({ tintColor, focused }) => (
        
      )
    }
  },
  MyPage: {
    screen: MyPage,
    navigationOptions: {
      tabBarLabel: '我的',
      tabBarIcon: ({ tintColor, focused }) => (
        
      )
    }
  }
};

export default class HomePage extends Component {
  constructor(props) {
    super(props)
    console.disableYellowBox = true
  }
  _TabNavigator () {
    return createAppContainer(createBottomTabNavigator(TABS))
  }
  render () {
    const Tabs = this._TabNavigator();
    return 
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f8f8f8'
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10
  }
})

Index页面顶部导航设计

import React, { Component } from 'react'
import { Button, Platform, StyleSheet, Text, View } from 'react-native'
import { createAppContainer, createMaterialTopTabNavigator } from 'react-navigation'
import navigationUtil from '../Navigator/navigationUtil'

export default class IndexPage extends Component {
  constructor(props) {
    super(props)
    this.tabNames = [
      'ios',
      'android',
      'nodejs',
      'vue',
      'react',
      'react native'
    ]
  }
  _genTabs () {
    const tabs = {}
    this.tabNames.forEach((item, index) => {
      tabs[`tab${index}`] = {
        screen: props => ,
        navigationOptions: {
          title: item
        }
      }
    })
    return tabs
  }
  render () {
    const TabNavigator = createAppContainer(
      createMaterialTopTabNavigator(this._genTabs(), {
        tabBarOptions: {
          tabStyle: {},
          upperCaseLabel: false,
          scrollEnabeld: true,
          style: {
            // 选项卡背景色
            backgroundColor: 'red'
          },
          indiccatorStyle: {
            // 指示器的样式
            height: 2,
            backgroundColor: '#fff'
          },
          labelStyle: {
            // 文字的样式
            fontSize: 16,
            marginTop: 6,
            marginBottom: 6
          }
        }
      })
    );
    return (
      
        
      
    )
  }
}

class IndexTab extends Component {
  render () {
    const { tabName } = this.props
    return (
      
        welcome to {tabName}
        
      
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f8f8f8'
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10
  }
})

Redux 与 React Navigation 结合集成

Redux + React Navigation 有点复杂,因为 Redux 是自顶向下管理一套状态, React Navigation 也是自顶向下管理一套状态甚至页面,这俩融合起来就有点困难了。

Redux不是必须的

要想撮合 Redux 和 React Navigation (为了方便描述,后面就直接称呼为导航),就得先各自了解在使用 React Navigation 的项目中,想要集成 redux 就必须要引入 react-navigation-redux-helpers这个库。

Redux

React 或者说 ReactNative 原生使用 State 和 props 管理 UI 状态,但是俩属性非常琐碎,稍微复杂一点的 UI 就没法应付,所以 Redux 应用而生,但是 Redux 又很乱,我觉得乱主要是以下原因:

  • 需要声明的东西多
  • 需要声明的地方多
  • API方法的命名很莫名其妙

流程,概念

React Native05_第1张图片
image.png
  • 用户(操作 View )发出 Action ,发出方式就用到了 dispatch 方法;
  • 然后, store 自动调用 Reducer ,并且传入两个参数(当前 State 和收到的 Action ),Reducer 会返回新的 State,如果有 middleware ,store会将当前 state 和收到的 action 传递给 middleware, middleware 会调用 reducer 然后返回新的 state
  • state 一旦有变化, store 就会调用监听函数,来更新 View

梳理

  • redux 是一个存储状态,响应事件动作( action )的地方,所以定义 redux 实现的叫 store
  • store 有一个初始状态( default state ),还有响应某个动作( action )的处理器( reducer )
  • 然后 UI 视图将这个 store 及其状态( state )和方法( action )注册到视图组件的 props,这样就可以在组件中取到这些状态和方法了
  • 当用户点击了某个操作后,会从 props 中拿到 action 并调用它,它会向 store 发送( dispatch )这个 action 的内容
  • 如果 store 中有中间件,会先逐个调用中间件来完成预处理
  • 然后再调用各个 reducer ,来完成状态的改变
  • 状态改变以后,因为状态绑定了 UI 组件的 props ,所以 react 会自动刷新 UI

概念

reducer:名字起源于 Array 的 reduce 方法,作者估计想表达的是遍历的意思,但是这个名字实在是诡异,所以我给他起名叫做处理器,或者叫事件触发器,作用就是 UI 发来 action 以后,他根据 action 的类型,对状态进行修改。他是 action 的消费者,他是个函数(或者专业点叫纯函数),但是他有个缺点,就是需要立即返回,如果是网络请求等异步操作,他就没法胜任了。

中间件:中间件的作用就是完成异步请求,或者完成其他一些需要封装起来的预处理,比如 redux-logger ,就是把 action 前后的状态打印出来的中间件,本质也是一个函数,但是结构很诡异,诡异程度类似于 C 语言中的3级指针,这个指针还尼玛是函数指针。不过这种诡异我们不需要操心,只需要填写内容

第一步:安装 redux , react-redux , react-navigation-redux-helpers

yarn add redux
yarn add react-redux // 因为 redux 其实是可以独立运行的js项目,所以把他使用在 react 项目中,还需要使用 react-redux
yarn add react-navigation-redux-helpers // 在使用 react navigation 的项目中,想要集成 redux 就必须要引入 react-navigation-redux-helpers 这个库

第二步:配置 Navigation

  • 引入 redux 和 react-navigation-redux-helpers
import {connect} from 'react-redux'
import {createReactNavigationReduxMiddleware, createReduxContainer} from 'react-navigation-redux-helpers'
  • 使用 createReduxContainer 方法,将 RootNavigator 封装成高阶组件 AppWithNavigationState ,这个高阶组件完成了 navigation prop 的替换,改成了使用 redux 里的 navigation
// 修改AppNavitor为RootNavigator并不在默认导出
export const RootNavigator=createAppContainer(
  createSwitchNavigator({
    Init:AppInitNavigator,
    Main:AppMainNavigator
  })
)

const AppWithNavigationState = createReduxContainer(RootNavigator,"root")
  • 创建导航中间件: createReduxContainer 把导航状态放到 props 里只是能被各个组件访问到,但是 React Navigation 还不能识别,所以还需要最后一步——创建一个中间件,把需要导航的组件与导航 reducer 连接起来
export const middleware = createReactNavigationReduxMiddleware(
  state => state.nav,
  'root'
)
  • 然后使用 Redux 的 connect 函数再封装一个高阶组件,默认导出
// state到props的映射关系
const mapStateToProps = state => {
  return {
    state:state.nav
  }
}

// 使用redux的connect函数再封装一个高阶组件,连接react组件与react store
export default connect(mapStateToProps)(AppWithNavigationState)
  • 完整代码
import { createStackNavigator, createAppContainer, craeteSwitchNavigator } from 'react-navigation'
import HomePage from '../Pages/HomePage'
import WelcomePage from '../Pages/WelcomePage'
import DetailPage from '../Pages/DetailPage'

// 引入redux
import { connect } from 'react-redux'

import {
  createReactNavigationReduxMiddleware,
  // reduxifyNavigator,react-navigation-redux-helpers 3.0变更,reduxifyNavigator被改名为createReduxContainer
  createReduxContainer
} from 'react-navigation-redux-helpers'

export const rootCom = "Init" // 设置根路由

const AppInitNavigator = createStackNavigator({
  WelcomePage: {
    screen: WelcomePage,
    navigationOptions: {
      header: null
    }
  }
});
const AppMainNavigator = createStackNavigator({
  HomePage: {
    screen: HomePage,
    navigationOptions: {
      header: null
    }
  },
  DetailPage: {
    screen: DetailPage
  }
});

export const RootNavigator = createAppContainer(
  createSwitchNavigator({
    Init: AppInitNavigator,
    Main: AppMainNavigator
  })
)
/*
  1. 初始化react-navigation与redux的中间件
    该方法的一个很大的作用就是为reduxifyNavigator的key设置actionSubscribers(行为订阅者)
*/

// react-navigation-redux-helpers3.0变更,createReactNavigationReduxMiddleware的参数顺序发生了变化
export const middleware = createReactNavigationReduxMiddleware(
  state => state.nav,
  "root"
)

/*
  2. 将跟导航器组件传递给reduxifyNavigator函数,
    并返回一个将navigation state和dispatch函数作为props的新组件;
    使用createReduxContainer方法,将RootNavigator封装成高阶组件
  AppWithNavigationState
    这个高阶组件完成了navigation prop的替换,改成了使用redux里的navigation
*/
const AppWithNavigationState = createReduxContainer(RootNavigator,'root')
// state到props的映射关系
const mapStateToProps=state=>{
  return{
    state:state.nav
  }
}

// 使用redux的connect函数再封装一个高阶组件,链接react组件与redux store
export default connect(mapStateToProps)(AppWithNavigationState)

第三步:配置 Reducer

import { combineReducers } from 'redux'
import theme from './theme'
import { rootCom, RootNavigator } from '../Navigator/AppNavigator'

// 1.指定默认state
const navState = RootNavigator.router.getStateForAction(
  RootNavigator.router.getActionForPathAndParams(rootCom)
)

/*
  上面的代码创建了一个导航action(表示我想打开rootCom),那么我们就可以通过action创建导航state,通过方法getStateForAction(action,oldNavigationState),俩参数,一个是新的action,一个是当前的导航state,返回新的状态,当没有办法执行这个action的时候,就返回null
*/

// 2. 创建自己的navigation reducer
const navReducer = (state = navState, action) => {
  const nextState = RootNavigator.router.getStateForAction(action, state)
  // 如果nextState为null或未定义,只需返回原始state
  return nextState || state
}

// 3. 合并reducer
// @type{Reducer|Reducer}
const index = combineReducers({
  nav: navReducer,
  theme: theme
})
export default index

第四步:配置 store

import { applyMiddleware, createStore } from 'redux'
import reducers from '../Reducer'
import { middleware } from '../Navigator/AppNavigator'

const middlewares = [middleware]
// 创建store
export default createStore(reducers, applyMiddleware(...middlewares))

第五步:在组件中应用

import React, { Component } from 'react'
import { Provider } from 'react-redux'
import AppNavigator from './Navigator/AppNavigator'
import store from './Store'

type Props = {}
export default class App extends Component{
  render () {
    // 将store传递给App框架
    return (
      
        
      
    )
  }
}

经过上述几步,我们已经完成了 react-navigation + redux 的集成,那么如何使用它呢?

案例:使用 react-navigation + redux 修改状态栏颜色

创建 Actions

// Types.js
export default {
  THEM_CHANGE: "THEM_CHANGE",
  THEM_INIT: "THEM_INIT"
}

创建 Actions/theme

import Types from '../Types'

export function onThemeChange (theme) {
  return {
    type: Types.THEM_CHANGE,
    theme: theme
  }
}

创建 Reducer/theme

import Types from "../../Actions/Types";

const defaultState = {
  theme: "blue"
};

export default function onAction(state = defaultState, action) {
  switch (action.type) {
    case Types.THEM_CHANGE:
      return {
        ...state,
        theme: action.theme
      };
    default:
      return state;
  }
}

在 reducer 中聚合

const index = combineReducers({
  nav: navReducer,
  theme: theme
});
  1. 订阅 state
import React, { Component } from "react";
import { Button, Platform, StyleSheet, Text, View } from "react-native";
import {
  createAppContainer,
  createMaterialTopTabNavigator
} from "react-navigation";
import IndexTab from "../Pages/IndexTab";
import { connect } from "react-redux";
import { onThemeChange } from "../Actions/theme";
import navigationUtil from "../Navigator/navigationUtil";

class IndexPage extends Component {
  constructor(props) {
    super(props);
    this.tabNames = [
      "ios",
      "android",
      "nodejs",
      "vue",
      "react",
      "react native"
    ];
  }
  _genTabs() {
    const tabs = {};
    this.tabNames.forEach((item, index) => {
      tabs[`tab${index}`] = {
        screen: props => ,
        navigationOptions: {
          title: item
        }
      };
    });
    return tabs;
  }
  render() {
    const TabBackground = this.props.theme;
    console.log(this.props);
    const TabNavigator = createAppContainer(
      createMaterialTopTabNavigator(this._genTabs(), {
        tabBarOptions: {
          tabStyle: {},
          upperCaseLabel: false,
          scrollEnabled: true,
          style: {
            // 选项卡背景色
            backgroundColor: TabBackground
          },
          indicatorStyle: {
            // 指示器的样式
            height: 2,
            backgroundColor: "#fff"
          },
          labelStyle: {
            // 文字的样式
            fontSize: 16,
            marginTop: 6,
            marginBottom: 6
          }
        }
      })
    );
    return (
      
        
      
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#f8f8f8"
  },
  welcome: {
    fontSize: 20,
    textAlign: "center",
    margin: 10
  }
});

const mapStateToProps = state => ({
  theme: state.theme.theme
});

export default connect(mapStateToProps)(IndexPage);

在上述代码中我们订阅了 store 中的 theme state ,然后该组件就可以通过 this.props.theme 获取到所订阅的 theme state 了。

  1. 触发 action 改变 state
import React, { Component } from "react";
import { Button, Platform, StyleSheet, Text, View } from "react-native";
import { connect } from "react-redux";
import { onThemeChange } from "../Actions/theme";

class IndexTab extends Component {
  render() {
    const { tabName } = this.props;
    return (
      
        welcome to {tabName}
        
        
      
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#f8f8f8"
  },
  welcome: {
    fontSize: 20,
    textAlign: "center",
    margin: 10
  }
});

const mapStateToProps = state => ({});

const mapDispatchToProps = dispatch => ({
  onThemeChange: theme => dispatch(onThemeChange(theme))
});
export default connect(mapStateToProps, mapDispatchToProps)(IndexTab);

API

combineReducers(reducers)

随着应用变得越来越复杂,可以考虑将 reducer 函数拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。

函数原型:combineReducers(reducers)

  • 参数:reducers(Object):一个对象,他的值( value )对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个,下面会介绍传入 reducer 函数需要满足的规则。
  • 每个传入 combineReducers 的 reducer 都需满足以下规则:
    • 所有未匹配到的 action ,必须把它接收到的第一个参数也就是那个 state 原封不动返回。
    • 永远不能返回 undefined ,当过早 return 时非常容易犯这个错误,为了避免错误扩散,遇到这种情况时 combineReducers 会抛异常
    • 如果传入的 state 就是 undefined ,一定要返回对应 reducer 的初始 state 。根据上一条规则,初始 state 禁止使用 undefined 。使用ES6的默认参数值语法来设置初始 state 很容易,但你也可以手动检查第一个参数是否为 undefined。

返回值

(Function):一个调用 reducers 对象里所有 reducer 的 reducer ,并且构造一个与 reducers 对象结构相同的 state 对象。

cobineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。

合并后的 reducer 可以调用各个子 reducer ,并把它们返回的结果合并成一个 state 对象。由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。

提示:在 reducer 层级的任何一级都可以调用 combineReducers。并不是一定要在最外层。实际上,你可以把一些复杂的子 reducer 拆分成单独的孙子级 reducer ,甚至更多层。

createStore

函数原型:createStore(reducer,[preloadedState],enhancer)

参数

  • reducer(Function):项目的根reducer。
  • [preloadedState](any):这个参数是可选的,用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致,那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
  • enhancer(Function):Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 相似,它也允许你通过复合函数改变 store 接口。

返回值

  • (Store):保存了应用所有 state 的对象。改变 state 的唯一方法是 dispatch action 。你也可以 subscribe 监听 state 的变化,然后更新UI。

示例

import { createStore } from "redux";
function todos(state = [], action) {
  switch (action.type) {
    case "ADD_TODO":
      return state.concat([action.text]);
    default:
      return state;
  }
}
let store = createStore(todos, ["Use Redux"]);
store.dispatch({
  type: "ADD_TODO",
  text: "Read the docs"
});
console.log(store.getState());
// [ 'Use Redux', 'Read the docs' ]

注意事项

  • 应用中不要创建多个 store 相反,使用 combineReducers 来把多个 reducer 创建成一个根 reducer。
  • 你可以决定 state 的格式。你可以使用普通对象或者 Immutable 这类的实现。如果你不知道如何做,刚开始可以使用普通对象。
  • 如果 state 是普通对象,永远不要修改它。比如, reducer 里不要使用 Object.assign(state,newData),应该使用 Object.assign({},state,newData)。这样才不会覆盖旧的 state。如果可以的话,也可以使用对象拓展操作符(object spread spread operator 特性中的return{...state,...newData})。
  • 对于服务端运行的同构应用,为每一个请求创建一个 store 实例,以此让 store 相隔离。dispatch 一系列请求数据的 action 到 store 实例上,等待请求完成后再在服务端渲染应用。
  • 当 store 创建后,Redux 会 dispatch 一个 action 到reducer 上,来用初始的 state 来填充 store。你不需要处理这个 action。但要记住,如果第一个参数也就是传入的 state 是undefined的话,reducer 应该返回初始的 state 值。
  • 要使用多个 store 增强器的时候,你可能需要使用 compose。

applyMiddleware

函数原型:applyMiddleware(...middleware)

使用包含自定义功能的 middleware 来扩展 Redux。

如何做到从不直接修改 state?

从不直接修改 state 是 Redux 的核心理念之一:为实现这个理念,可以通过以下两种方式:

1.通过Object.assign()创建一个对象拷贝,而拷贝中会包含新创建或更新过的属性值

在下面的 todoApp 示例中,Object.assign() 将会返回一个新的 state 对象,而其中的 visibilityFilter 属性被更新了:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      });
    default:
      return state;
  }
}

2. 通过ES7的新特性[对象展开运算符(Object Spread Operator)]

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return { ...state, visibilityFilter: action.filter };
    default:
      return state;
  }
}

这样你就能轻松的跳回到这个对象之前的某个状态(想象一个撤销功能)。

总结

  • Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合,而不是创建多个 store;
  • redux 一个特点是:状态共享,所有的状态都放在一个 store 中,任何 component 都可以订阅 store 中的数据;
  • 并不是所有的 state 都适合放在 store 中,这样会让 store 变得非常庞大,如某个状态只被一个组件使用,不存在状态共享,可以不放在 store 中。

RN网络编程

RN 提供了和 web 标准一致的 Fetch API,用于满足开发者访问网络的需求。

发起请求

要从任意地址获取内容的话,只需简单地将网址作为参数传递给 fetch 方法即可(fetch 这个词本身也是获取的意思)

fetch('https://mywebsite.com/mydata.json');

Fetch 还有可选的第二个参数,可以用来定制HTTP请求一些参数。你可以指定 header 参数,或是指定使用 post 方法,又或是提交数据等等:

fetch("https://mywebsite.com/endpoint/", {
  method: "POST",
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    firstParam: "yourValue",
    secondParam: "yourOtherValue"
  })
});

提交数据的格式关键取决于 headers 中的 Content-type。content-type 有很多种,对应 body 的格式也有区别。到底应该采用什么样的 Content-type 取决于服务器端,所以请和服务器端的开发人员沟通清楚。常用的 Content-type 除了上面的 application/json,还有传统的网页表单形式,示例如下:

fetch("https://mywebsite.com/endpoint/", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "key1=value1&key2=value2"
});

Fetch 方法会返回一个 Promise,这种模式可以简化异步风格的代码。

function getMoviesFromApiAsync() {
  return fetch("https://facebook.github.io/react-native/movies.json")
    .then(response => response.json())
    .then(responseJson => {
      return responseJson.movies;
    })
    .catch(error => {
      console.error(error);
    });
}

你也可以在 RN 应用中使用 ES2017 标准中的 async/await 语法:

// 注意这个⽅法前⾯有async关键字
async function getMoviesFromApi() {
 try {
 // 注意这⾥的await语句,其所在的函数必须有async关键字声明
 let response = await fetch(
 'https://facebook.github.io/react-native/movies.json',
 );
 let responseJson = await response.json();
 return responseJson.movies;
 } catch (error) {
 console.error(error);
 }
}
//fetch("https://api.douban.com/v2/movie/top250")

别忘了 catch fetch 可能抛出的异常,否则出错时你可能看不到任何提示

注意:使用 Chrome 调试目前无法观测到 RN 中的网络请求,你可以使用第三方的 react-native-debugger 来进行观测。

你的赞是我前进的动力

求赞,求评论,求分享...

你可能感兴趣的:(React Native05)