React Native - 从 Redux 进阶谈起

作者: aJIEw | 来源:发表于2018-10-12 14:17 被阅读18次
Reeds in the Autumn Wind

继上次写完 Redux 之后,留下了很多坑,其实这篇也不算是进阶,毕竟只是一些库的使用以及一些小技巧而已,权当是上一篇的填坑了吧。

01 Middleware & Thunk

What's Middlewares?

当我们发送 action 的时候,正常情况下是从 action -> reducer,引入中间件后变为 action -> middlewares -> reducer。为什么要这么做?因为很多时候我们想在 action 传送到 reducer 之前,对数据流进行改变或者进行一些别的什么操作,比如对 action 添加日志,如果一个个去更改 Action Creator 未免太过麻烦,而使用 Middleware 的话就简单很多,可以帮我们省去很多重复代码。最重要的一点,我们可以通过使用 Thunk Middleware 来实现异步 action。

What’s a thunk?

Thunk 就是包装了函数表达式的用于延缓求值的方法。通过下面这个例子可以很好的理解:

// 立即计算求值 1+2
let x = 1 + 2;

// 计算被延迟了,foo 可以在稍后被调用的时候再去计算 1+2,这个时候 foo 方法就是一个 thunk 方法
let foo = () => 1 + 2;

为什么叫 thunk?它是『think』过去式的 幽默式表达(意思是已经想好怎么做了,但是就是还没做 o(´^`)o)。

Redux Thunk

当我们使用了 middleware 的时候,发出的 action 不会直接被 reducer 处理掉,而是先被 middleware 截获,并且我们可以在 middleware 中发起 异步请求。Redux Thunk 就是这样的中间件,允许我们在 Redux 中发起异步请求。

applyMiddleware

createStore() 的时候,我们可以指定 enhancer 参数,最常见的就是 applyMiddleware(),为了在 Redux 中开启对 Redux Thunk 的支持,就需要使用该方法:

import {applyMiddleware, combineReducers, createStore} from "redux";
import thunkMiddleware from 'redux-thunk';
import textReducer from "./reducers/changeText";

const allReducers = combineReducers({textReducer});
let store = createStore(allReducers, applyMiddleware(thunkMiddleware));

export default store;
Return a function

使用了 Redux Thunk 后,我们可以在 Action Creators 中返回一个方法而不是 action,因此我们可以延迟 action 的 dispatch 或者在只有满足条件的时候才 dispatch。内部返回的方法接收的参数分别是 store 的 dispatchgetState 方法。举个发起请求获取数据的例子:

export const FETCH_DATA_IN_PROGRESS = 'FETCH_DATA_IN_PROGRESS';
export const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS';
export const FETCH_DATA_Failure = 'FETCH_DATA_Failure';

function getData(url) {
  return async (dispatch, getState) => {
    
    let param = getState().getData.param;
    
    dispatch({type: FETCH_DATA_IN_PROGRESS});

    // request 是封装的 fetch 请求
    const res = await request.post(url, {param});

    if (res && !res.status) {
      // 请求成功才发送数据
      dispatch({type: FETCH_DATA_SUCCESS, data: res.data});
    } else {
      dispatch({type: FETCH_DATA_Failure});
    }
  }
}

上面这个就是一个 thunk function,其使用方式与普通的 Action Creators 一样,可以直接作为第二个参数传入 connect() 方法中:

export default connect(mapStateToProps, {changeText, changeBack, getData})(Main);

当然 Redux Thunk 也不是唯一的发送异步 Action 的方式,比如 redux-saga 以及 redux-promise 都可以达到相同的目的,Redux 文档上还介绍了一些别的方式:

Thunk middleware isn't the only way to orchestrate asynchronous actions in Redux:

It is up to you to try a few options, choose a convention you like, and follow it, whether with, or without the middleware.

02 Higher-Order Components

What's HOC?

HOC 即 Higher-Order Components 高阶组件的简称,从形式上来看,其实就是接收函数作为参数的函数。

HOC 是 React 中的一种模式,通过 HOC 我们可以方便地在多个组件中注入一些通用的功能,这样就可以避免重复的代码逻辑。一个 HOC 函数接收一个组件作为参数,并且返回一个新的组件,通过 HOC 函数我们可以为组件添加额外的功能或者数据。

在 RN 中,一种常见的使用方式是通过 HOC 函数作为页面跳转的依据。比如检验有无登录,如果页面需要登录后才能查看,那么用户在未登录的情况下会先跳转登录页。

How to use it?

HOC 的使用方法很简单,形式如下:

const hoc = (WrappedComponent) => {
  class HOC extends React.Component {
    render() {
      return <WrappedComponent />;
    }
  }
  return HOC;
};

这里的匿名函数接收的参数是 WrappedComponent,即我们需要包装的组件,返回的是我们对包装的组件进行处理之后的新组件。其使用方式如下:

const myHOC = hoc(MyComponent);

除此之外,我们也可以结合 React Redux 使用:

const hoc = (WrappedComponent) => connect(mapStateToProps, mapDispatchToProps)(
  class HOC extends React.Component {
    render() {
      return <WrappedComponent />;
    }
  }
 );

其实这里的 connect() 方法就是一个 HOC 的例子,通过连接组件和保存在 Store 中的全局 state,同时在组件中可以通过 props 的形式来访问这些全局 state。

03 Combine with react-native-router-flux

介绍了 Thunk Middleware 和 HOC,接下来我想用一个例子展示如何在项目中使用他们。这个例子使用到了 React Native Router,这是一个非常好用的页面路由、页面导航以及页面间传递数据的 RN 框架。这里我只用到了其中很小一部分的功能,更多的用法请移步 API 文档

照惯例,先看下实现的效果:Demo GIF

可以看到,我们在原来的基础上添加了两个新的页面,一个是登录后的页面,一个是登录页,连接它们的是一个 HOC 函数:

const verifyLogin = WrappedComponent => connect(mapStateToProps)(
  class extends Component {
    render() {
      if (this.props.authToken) {
        return (<WrappedComponent {...this.props} />);
      } else {
        return (<Login/>);
      }
    }
  }
);

function mapStateToProps(state) {
  return {
    authToken: state.authInfo.data,
  };
}

export default verifyLogin;

在 HOC 中,我们根据 authToken 的状态来决定是直接跳转还是先跳转到登录页。这个例子很好的说明了 HOC 函数的优势,所有需要登录的地方都只要调用这个函数就可以了。这里的 authToken 是从 store 中获取的,因此我们还得写一个模拟登录的 Action Creator 来进行登录获取 token,同时展示下如何使用 Redux Thunk 发起异步请求:

export function login(info) {
  return async (dispatch, getState) => {
    dispatch({type: Request_login_requesting});

    // 模拟发起请求并获取结果
    let res = 'fakeAuthRequestAndGetResult';

    // 根据请求结果发送不同的 action
    if (res) {
      dispatch({type: Request_login_success, data: res});
    } else {
      dispatch({type: Request_login_failure});
    }
  }
}

另外,还有对应的 reducer,注意,combineReducers() 接收的参数为对象:

function data(state = '', actions) {
  switch(actions.type){
    case Request_login_success:
    case Request_login_failure:
      return actions.data;
    case Request_logout:
      return '';
    default:
      return state;
  }
}

let authInfo = combineReducers({data});

export default authInfo;

最后,除了两个新页面之外,我们还需要定义一个 router 页,也就是使用 React Native Router 来管理各个页面:

class AppRouter extends Component {
  render() {
    return <Router>
      <Stack>
        <Scene key='root' component={Main} />
        <Scene key='personal' component={Personal} />
        <Scene key='login' component={Login} />
      </Stack>
    </Router>
  }
}

export default AppRouter;

然后在 App 的入口处使用 AppRouter 替换原来的 Main:

export default class Root extends Component {
  render() {
    return (
      // 第一层包装,连接组件和 store
      <Provider store={store}>
        <AppRouter/>
      </Provider>
    )
  }
}

OK,核心代码就是这样了,完整代码:aJIEw/Redux

04 Sum Up

这篇写的比较杂,一开始只是想写下 Redux 中的 middleware,后来看了这篇 Redux-Thunk vs. Redux-Saga,发现对于大多数场景下,的确使用 Redux-Thunk 就足够了。而 HOC 也是临时想到要写一写的,毕竟也算是 React 中一种常见的模式了吧。最后这个结合 react-native-router 写的例子也比较简单,主要用来说明 Redux-Thunk 发异步请求以及 HOC 的大致用法。

好了,写完这篇有种『我已经掌握 React Native 开发了』的错觉,但其是内心还是很慌的,因为知道要掌握的东西还有太多。不过一口吃不成个胖子,只能静下心来一步一脚印慢慢往前走了。每天进步一点点,坚持下去,收获就是巨大的。嗯,加油~(•̀ᴗ•́)و ̑̑


相关文章:React Native - Redux 入门

参考文章:
  1. React 项目中 Redux 中间件的理解
  2. Redux 入门教程(二):中间件与异步操作
  3. Redux-Thunk 快速入门
  4. Understanding how redux-thunk works
  5. 面向初学者的高阶组件教程
  6. Understanding React Higher-Order Components by Example

相关文章

网友评论

    本文标题:React Native - 从 Redux 进阶谈起

    本文链接:https://www.haomeiwen.com/subject/nktgaftx.html