[React]React-Redux用法

来源:
Redux入门教程-阮一峰
Redux中文文档

React-Redux是Redux对于React的封装

UI组件

React-Redux将所有的组件分为两大类,UI组件和容器组件。
UI组件将不带有任何逻辑,无状态,所有的参数由this.props提供且不使用Redux的API
例如:

const Title=
    value=><h1>{value}</h1>;

UI组件不含有状态,因此也被称为纯组件,与纯函数相同,也由参数决定其值。

容器组件

容器组件负责管理数据和状态,不呈现UI,且使用Redux‘s API

在大多数情况下要将组件进行拆分,使其由外面包裹的容器组件和内部的UI组件构成。外部的容器组件负责与外部的通信,将数据传给UI组件,UI组件负责渲染出视图。
React-Redux规定,所有的UI组件都由用户提供,容器组件是由React-Redux自动生成的。

connect()

React-Redux提供connect()方法,用于从UI组件生成容器组件。

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

上面的代码将UI组件TodoList通过connect()生成VisibleTodoList容器组件。

需要为了容器组件定义业务逻辑:

  • 输入逻辑:外部的数据转换为UI组件的参数
  • 输出逻辑:用户的动作变为Action对象,从UI组件传出。

connect的完整API如下:

import {connect} from ‘react-redux‘

const VisibleTodoList=connect(
    mapStateToProps,
    mapDispatchToProps
)(TodoList)

在上面的代码中,connect()接收了两个参数:mapStateToprops和mapDispatchToProps。它们定义了UI组件的业务逻辑。

mapStateToProps()

mapStateToProps是一个函数。它的作用是建立一个从外部state对象到UI组件的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组件的重新渲染。
mapStateToProps的第一个参数是state对象,第二个参数代表容器组件的props对象。
在使用第二个参数之后,如果容器组件的参数发生变化,将会引发UI组件重新渲染。

connect方法可以忽略mapStateToProps参数,这时Store的更新不会引起UI组件的更新。

mapDispatchToProps()

mapDispatchToProps()是connect()函数的第二个参数,用来建立UI组件的参数到store.dispatch()方法的映射。它定义了哪些用户的操作应该作为Action传给Store进行处理。
它可以是函数或者对象。
如果它是函数,它的入参为dispatch和ownProps(容器组件的props对象)两个参数。

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

mapDispatchToProps作为函数返回了一个对象,该对象的每个键值对定义了UI组件的参数如何发出Action。

mapDispatchToProps是一个对象时,它的每个键名是对应的UI组件的同名参数,键值为一个函数(Action creator),返回的Action将会由Redux自动发出。

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

组件

connect()方法生成容器组件后,需要使容器组件得到state才能生成UI组件的参数。

为了避免一级级地传递state导致的不必要的麻烦,使用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‘)

在上面的例子中,App组件的所有子组件默认可以得到state。
原理是React组件的context机制。

计数器实例

地址

import React, { Component } from ‘react‘
import PropTypes from ‘prop-types‘
import ReactDOM from ‘react-dom‘
import { createStore } from ‘redux‘
import { Provider, connect } from ‘react-redux‘

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

Counter.propTypes = {
  value: PropTypes.number.isRequired,
  onIncreaseClick: PropTypes.func.isRequired
}

// Action
const increaseAction = { type: ‘increase‘ }

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

// Store
const store = createStore(counter)

// Map Redux state to component props
function mapStateToProps(state) {
  return {
    value: state.count
  }
}

// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
  return {
    onIncreaseClick: () => dispatch(increaseAction)
  }
}

// Connected Component--容器组件
const App = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

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

另一个例子

将之前提过的面包屑实例进行完善:
在这个例子中,需要在切换路由的时候,将state中的当前菜单名字状态赋给面包屑UI组件的props,进一步重新渲染UI;
在切换路由的子组件中,需要在路由变化时发送Action改变Store;

import {switchMenu} from ‘./../../redux/action‘
import {connect} from ‘react-redux‘
//...

class Navleft extends React.Component{
    //...
    handleOnClick=({item}){
        const {dispatch}=this.props;
        //产生Action改变Store
        dispatch(switchMenu(item.props.title));
    }
    render(){
        return (
            //...
            <Menu 
                onClick={this.handleOnClick}
                
                    {...}
                </Menu>
);
}
}
//连接
export default connect()(Navleft);

以上的代码在路由菜单切换时产生Action改变Store。
接下来要在Store改变时重新渲染面包屑UI

import {connect} from ‘react-redux‘
class Header extends React.Component{
   render() {
        return (
            //...
        );
    }
}
//connect参数,通过state影响props
const mapStateToProps=state=>{
    return{
        menuName:state.menuName
    }
}
//连接
export default connect(mapStateToProps)(Header)

在以上的例子中,在UI组件中通过{this.props.menuName}就可以获得在容器组件中的状态。
这样就达到了在切换菜单时改变面包屑显示的目的。

相关推荐