参考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, 如下所示:
然后再次运行,又遇到另一个问题
解决办法:
找到这个文件,删除再添加。
重新编译运行就可以了。app的入口是index.js文件:
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('RNApp', () => App);
在index.js中,将App注册为项目的根组件,运行项目后首先看到的是App.js渲染出的页面。
接下来加入路由跳转,网络请求和redux管理数据。
总的文件目录结构如下:
搭建好一个新项目之后,要使用导航实现页面跳转,可以使用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.js和DetailScreen.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:
首先安装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 是一个 JavaScript 的状态管理库。使用前要装redux,react-redux,redux-devtools开发者工具和redux-thunk中间件:
yarn add redux
yarn add react-redux
yarn add redux-devtools
yarn add redux-thunk
connect装饰器的作用是,使该页面可以监听到store的变化。首先安装:
yarn add babel-plugin-transform-decorators-legacy
装好包之后在APP的.babelrc文件中配置:
然后就可以在页面中调用了。在页面中加入connect装饰器之后,这个页面相关的数据变了就会重新渲染页面组件。调用方式:
@connect(state => ({
detailData: state.detailData,
}))
connect的原理详解:https://www.jianshu.com/p/9873d4ccb891
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>
);
}
}