Redux学习总结三

参考自阮一峰React教程

react-redux的用法

一、UI组件

react-redux将所有组件分成两大类:UI组件(presentational component)和容器组件(container component)。
UI组件有以下几个特征:
1. 只负责UI的呈现,不带有任何业务逻辑
2. 没有状态(即不使用this.state这个变量)
3. 所有数据都由参数(this.props)提供
4. 不使用任何Redux的API

二、容器组件

容器组件的特征如下:
1. 负责管理数据和业务逻辑,不负责UI的呈现
2.带有内部逻辑
3. 使用redux的API

若一个组件既有UI又有业务逻辑,那就将它拆分成下面的结构:外边是一个容器组件,里面包含了一个UI组件,前者负责与外传给部的通信,将数据传给后者,由后者渲染出视图。

三、connect()

react-redux提供connect方法,用于从UI组件生成容器组件。

import { connect } from 'react-redux'
const VisibleTodoList = connect()(TodoList);

上面代码中TodoList是UI组件,VisibleTodoList就是由react-redux通过connect方法自动生成的容器组件。
为了定义业务逻辑,需给出以下两方面的信息:
1. 输入逻辑:外部的数据(即state对象)如何转换为UI组件的参数。
2. 输出逻辑:用户发出的动作如何变为Action对象,从UI组件传出去。

//connect方法的完整API
import { connect } from 'react-redux'
const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

注:connect方法接受两个参数:mapStateToPropsmapDispatchToProps,前者负责输入输入逻辑,即将state映射到UI组件的参数props,后者负责输出逻辑,即将用户对UI组件的操作映射为Action.

四、mapStateToProps()

mapStateToProps是一个函数,用于建立一个从state对象到props对象的映射关系,它返回一个对象,里面的每一个键值对就是一个映射。

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

上面代码中,mapStateToProps是一个函数,接受state作为参数,返回一个对象,这个对象有一个todos属性,代表UI组件的同名参数,后面的getVisibleTodos也是一个函数,可以从state算出todos的值。

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
    default:
      throw new Error('Unknown filter: ' + filter)
  }
}

mapStateToProps会订阅Store,每当state更新的时候,就会自动执行,重新计算UI组件的参数,从而触发UI组件的重新渲染。

五、mapDispatchToProps()

mapDispatchToProps是connect函数的第二个参数,用来建立UI组件的参数到store.dispatch方法的映射,它可以是一个函数,也可以是一个对象。
1. mapDispatchToProps是一个函数,会接受dispatch和ownProps两个参数。

const mapDispatchToProps = (
  dispatch,
  ownProps
) => {
  return {
    onClick: () => {
      dispatch({
        type: 'SET_VISIBILITY_FILTER',
        filter: ownProps.filter
      });
    }
  };
}

2. mapDispatchToProps是一个对象,它的每个键名也是对应UI组件的同名参数,键值是一个函数,被当做Action creator,返回的Action会由Redux自动发出。

const mapDispatchToProps = {
  onClick: (filter) => {
    type: 'SET_VISIBILITY_FILTER',
    filter: filter
  };
}

六、组件

react-redux提供Provider组件,可以让容器组件拿到state.

import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp);
render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

上面代码中,Provider在根组件外边包了一层,这样APP的所有子组件就默认可以拿到state了。
它的原理就是react组件的context属性:

class Provider extends Component {
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children;
  }
}
Provider.childContextTypes = {
  store: React.PropTypes.object
}

上面代码中,store放在了上下文对象context上面,然后子组件就可以从context拿到store.

class VisibleTodoList extends Component {
  componentDidMount() {
    const { store } = this.context;
    this.unsubscribe = store.subscribe(() =>
      this.forceUpdate()
    );
  }

  render() {
    const props = this.props;
    const { store } = this.context;
    const state = store.getState();
    // ...
  }
}

VisibleTodoList.contextTypes = {
  store: React.PropTypes.object
}

七、实例:计数器

1. 纯的计数器UI组件

class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    )
  }
}

上面代码中,UI组件有两个参数:value和onIncreaseClick,前者需要从state计算得到,后者需要向外发出Action.
2. 定义state到value的映射,以及onIncreaseClick到dispatch的映射。

function mapStateToProps(state) {
  return {
    value: state.count
  }
}

function mapDispatchToProps(dispatch) {
  return {
    onIncreaseClick: () => dispatch(increaseAction)
  }
}

// Action Creator
const increaseAction = { type: 'increase' }

3. 使用connect方法生成容器组件。

const App = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

4. 定义这个组件的Reducer

// Reducer
function counter(state = { count: 0 }, action) {
  const count = state.count
  switch (action.type) {
    case 'increase':
      return { count: count + 1 }
    default:
      return state
  }
}

5. 生成store对象,并使用Provider在根组件外面包一层。

import { loadState, saveState } from './localStorage';

const persistedState = loadState();
const store = createStore(
  counter,
  persistedState
);

store.subscribe(throttle(() => {
  saveState({
    count: store.getState().count,
  })
}, 1000))

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

八、react-router路由库

const Root = ({ store }) => (
  <Provider store={store}>
    <Router>
      <Route path="/" component={App} />
    </Router>
  </Provider>
);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值