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