ReduxReact Native

RN学习笔记2-Redux

2018-07-10  本文已影响131人  Realank

一、背景

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

下面就来总结一下使用

二、安装

需要在项目里添加如下依赖(版本无所谓)

    "react-redux": "^5.0.7",
    "redux": "^4.0.0",
    "redux-logger": "^3.0.6"

因为redux其实是可以独立运行的js项目,所以把他使用在react项目中,还需要使用react-redux,
redux-logger是打印redux事件log的中间件,具体内容我们后面会说

三、大牛文章

我主要是看这几个文章入门的
https://segmentfault.com/a/1190000008741380
https://www.cnblogs.com/hhhyaaon/p/5860159.html
https://codesandbox.io/s/6n20nrzlxz c11(讲pure redux)
https://segmentfault.com/a/1190000008322583

四、原理

借鉴于https://segmentfault.com/a/1190000008322583

那么我们来仔细说一下reducer和中间件
reducer:名字起源于Array的reduce方法,作者估计向表达的是遍历的意义,但是这个名字实在是诡异,所以我给他起名叫做处理器,或者叫事件触发器,作用就是UI发来action以后,它根据action的类型,对状态进行修改。他是action的消费者,他是个函数(或者专业点叫纯函数),但是他有个缺点,就是需要立即返回,如果是网络请求等异步操作,他就没法胜任了。
中间件:中间件的作用就是完成异步请求,或者完成其他一些需要封装起来的预处理,比如redux-logger,就是把action前后的状态打印出来的中间件,本质也是个函数,但是结构很诡异,诡异程度类似于C语言中的3级指针,这个指针还尼玛是函数指针。不过这种诡异我们不需要操心,只需要填写内容

五、RTFSC

(一)redux创建

仿照大牛们的例子,我们做个通过加减按钮改变数值的功能

  1. 首先我们引入redux模块
import { combineReducers, createStore, applyMiddleware, compose } from 'redux'
  1. 创建action creator
    action其实就是个对象,有一个最基本的key是type,表示action的类型,如果业务需要,还可以增加其他key,反正这个对象怎么用也是你自己的事,根据自己喜好来。
    而所谓action creator就是个创建action对象的函数
// action types
export const INCREASE = 'INCREASE'
export const DECREASE = 'DECREASE'
export const RESET = 'RESET'

// actions
const increase = () => ({ type: INCREASE })
const decrease = () => ({ type: DECREASE })
const reset = (num) => ({ type: RESET, num })//除了type,你还可以加别的内容

  1. 定义初始状态
const defaultState = {
  count: 5
}
  1. 创建reducer
    前面说了,reducer其实就是个函数,接受两个参数,一个是当前状态,一个是发来的action,然后返回一个新的状态
function counter (state = defaultState, action) {//有个默认参数,当第一次调用的时候,使用初始状态
  switch (action.type) {
    case INCREASE:
      return { ...state, count: state.count + 1 }
    case DECREASE:
      return { ...state, count: state.count - 1 }
    case RESET:
      return { ...state, count: action.num }
    default:
      return state
  }
}
  1. 创建store
const reducers = combineReducers({counter})

const configureStore = preloadedState => {
  return createStore(
    reducers,
    preloadedState,
    compose(
      applyMiddleware(createLogger)
    )
  )
}
const store = configureStore()
  1. 最后,我们把外界需要的变量导出
export {store, increase, decrease, reset}

store需要放到Provider组件里,包在我们的页面上,用于往页面的props里注入属性和action方法,后面一个就是action creator了,外界调用这个方法来改变store的状态

(二)redux使用

我看到的demo里,都是使用页面根节点来绑定store,根据我的理解,我觉得每个功能点的逻辑,最好分开,所以把一整个store以及所有state定义在根节点,无论对代码整理还是性能都是不可接受的,所以我google了很久终于在so上找到了解决方案。

  1. 首先定义一个页面叫做ReduxPage:
class ReduxPage extends Component {
  constructor (props) {
    super(props)
    this.state = {}
  }

  render () {
    const {increase, decrease, reset} = this.props
    return (
      <View style={styles.container}>
        <Text style={styles.counter}>{this.props.counter.count}</Text>
        <SubText {...this.props} />
        <TouchableOpacity style={styles.btn} onPress={() => { reset(0) }}>
          <Text>归零</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.btn} onPress={increase}>
          <Text>加1</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.btn} onPress={decrease}>
          <Text>减1</Text>
        </TouchableOpacity>
      </View>

    )
  }
}

一个Text显示计数器数字,一个SubText展示子组件显示计数器数字
三个按钮展示动作action

  1. 然后绑定store的state和action方法到这个页面
const mapStateToProps = state => ({
  counter: state.counter
})
const mapDispatchToProps = dispatch => (bindActionCreators({increase, decrease, reset}, dispatch))
//或者const mapDispatchToProps = {increase, decrease, reset}
let Container = connect(mapStateToProps, mapDispatchToProps)(ReduxPage)
  1. 导出页面组件

我们不能直接导出刚刚创建的ReduxPage,因为现在还没有人绑定store呢,使用下面的代码来绑定store并导出组件

export default class extends Component {
  render () {
    return (<Provider store={store}>
      <Container />
    </Provider>)
  }
};

在其他地方,比如父组件,想怎么用就怎么用就可以了。

(三)redux难题

  1. 自定义子组件继承父组件的redux props
    我们在redux connect的那个组件上,因为有mapStateToProps和mapDispatchToProp函数,可以在其this.props里获取到映射的状态和action,但是如果在这个组件上再嵌套一个自定义的子组件(例如我定义的SubText),在子组件里就获取不到状态和action了,但是我看教程里明明说的是可以自动往下层传的,所以我这里使用了这样的语法,也是google的
 <SubText {...this.props} />

我觉得这只能算是个workaround,正规的写法是啥还没搜出来

  1. 映射方法的时候,定义一个key
    如果我们不想污染this.props,像state一样可以通过this.props.<key>.xxx来获取方法,可以这样写
const mapDispatchToProps = dispatch => ({businessDispatch: bindActionCreators({sortChange, filterChange, fetchData}, dispatch)})
  1. React Navigation

redux和redux navigation合起来使用会比较麻烦,官方有教程
但是因为我的redux只是我当前一个页面的业务redux,没必要和redux navigation混合起来,所以我的解决方案:最外层是react navigation,内层是redux

export default class extends Component {
  static navigationOptions = ({ navigation }) => {
    const params = navigation.state.params || {}

    return {
      title: '客户列表',
      headerRight: (//导航栏按钮
        <View style={{flexDirection: 'row'}}>
          <TouchableOpacity
            style={{width: 40, height: 40, alignItems: 'center', justifyContent: 'center'
            }}
            onPress={params.search}
          >
            <Image source={require('../resource/search.png')} style={{width: 20, height: 20}} />
          </TouchableOpacity>
          <TouchableOpacity
            style={{width: 40, height: 40, alignItems: 'center', justifyContent: 'center'
            }}
            onPress={params.showMore}
          >
            <Image source={require('../resource/more.png')} style={{width: 20, height: 20}} />
          </TouchableOpacity>
        </View>

      )
    }
  };
  render () {
    return (<Provider store={store}>
      <Container navigation={this.props.navigation} />
    </Provider>)
  }
};

  1. 自己创建中间件
    初次看到中间件的时候感觉好高深,好难懂,好晦涩,其实根本就不是,js让我这个objcer懵逼的地方就在于乱七八糟的风格,这个本质就是个函数,其余都可以忽略
    我现在就遇到一个情况,需要下拉刷新请求数据,这是个异步操作,reducer没法处理,所以用中间件。
    这个方法拦截Action.FETCH_DATA(其实就是个提前定义好的字符串,表示具体action type) action, 然后延时刷新数据,刷新数据前后向store发送处理数据中的状态,所谓的刷新数据就是个延时。
function createFetchCustom ({ dispatch, getState }) {
  return (next) =>
    (action) => {
      const prevState = getState()
      const returnValue = next(action)
      const nextState = getState()
      const actionType = String(action.type)
      const customState = nextState.customerListBusiness
      if (actionType === Action.FETCH_DATA) {
        console.log('middle ware fetch data')
        dispatch(Action.processingdata(true))
        const customs = [
          {key: '1', name: '刘安博'},
          {key: '2', name: '张土豪'},
          {key: '3', name: '李贫农'}
        ]
        setTimeout(() => {
          dispatch(Action.reloadData(customs))
          dispatch(Action.processingdata(false))
        }, 1000)
      }

      return returnValue
    }
}

可以看到,这是个“三级函数”,但是因为这个函数是redux内部调用的,所以我们不需要关心他的复杂度,
在上面的代码中,我已经获取到了action发送前后的状态,action的类型,所以你就根据action做你爱做的事情就可以了。

上一篇下一篇

猜你喜欢

热点阅读