本篇使用redux结合react重写刚才那个很简单的hello world示例。

redux的理念

redux有三个重要的理念:单一数据源、状态是只读的、使用纯函数转换状态。具体见链接

安装redux与react-redux

npm install redux react-redux --save

状态转换纯函数

web-src/js/components/GreetingConstant.js

export const CHANGE_NAME = 'CHANGE_NAME';

web-src/js/reducers/GreetingReducer.js

import {CHANGE_NAME} from '../constants/GreetingConstant.js'

const initialState = {
  name: '',
  output: ''
}

export function GreetingReducer(state = initialState, action) {

    if (typeof state === 'undefined') {
        return initialState;
    }

    switch(action.type) {
        case CHANGE_NAME:
            return Object.assign({}, state, {
                name : action.name,
                output: 'Hello, ' + action.name
            });
        default:
            return state;
    }
};

这两个文件很简单,GreetingConstant.js里定义了action类型的常量,GreetingReducer.js就是一个普通纯函数,它的工作就是根据action转换state。

actionCreator

action是一个纯对象,其中保存了用来转换state的信息,一般包括type类型及其它参数,官方是这样定义的Actions are payloads of information that send data from your application to your store.

actionCreator则是产生action的方法。

web-src/js/actions/GreetingAction.js

import { CHANGE_NAME } from '../constants/GreetingConstant.js'

export function changeName(name) {
    return {
        type : CHANGE_NAME,
        name: name
    };
}

组件

在redux与react项目中,组件分为Presentational ComponentsContainer Components,有的地方叫Dump ComponentsSmart Components

| | Presentational Components | Container Components | |-|-|-| |Purpose|How things look (markup, styles)| How things work (data fetching, state updates)| |Aware of Redux|No| Yes| |To read data|Read data from props| Subscribe to Redux state| |To change data|Invoke callbacks from props| Dispatch Redux actions| |Are written|By hand| Usually generated by React Redux|

简单来说Presentational Components是完全根据props属性决定行为与展现的组件,完成不感知redux的存在。Container Component则负责从state中抽取属性,分发redux's action,这里一般会用到reduxconnect方法,还是看下面的代码。

web-src/js/components/GreetingComponent.js,这个就是一个Presentational Components组件

import React from 'react'

const noop = function(){};

class GreetingComponent extends React.Component{

    constructor(props){
        super(props);
        this._changeName = this.changeName.bind(this);
    }

    changeName(e){
        this.props.changeName(e.target.value);
    }

    render() {
        return (
            <div>
                <input value={this.props.name} onChange={this._changeName}/><br/>
                <label>{this.props.output}</label>
            </div>
        );
    }
};

GreetingComponent.propTypes = {
    changeName: React.PropTypes.func.isRequired,
    name: React.PropTypes.string.isRequired,
    output: React.PropTypes.string.isRequired
};

GreetingComponent.defaultProps = {
    changeName: noop,
    name: '',
    output: ''
};

export default GreetingComponent;

web-src/js/containers/GreetingContainer.js,这个就是一个Container Component组件

import { connect } from 'react-redux'

import GreetingComponent from '../components/GreetingComponent.js'

import {changeName} from '../actions/GreetingAction.js'

const mapStateToProps = (state) => {
  return {
    name: state.name,
    output: state.output
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    changeName: (name) => {
      dispatch(changeName(name))
    }
  }
}

const GreetingContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(GreetingComponent)

export default GreetingContainer

web-src/js/components/GreetingApp.js,这个就是一个Presentational Components组件

import React from 'react'

import GreetingContainer from '../containers/GreetingContainer.js'

export default class GreetingApp extends React.Component{
    render(){
        return <GreetingContainer/>
    }
}

使用Provider将state与组件关联起来

web-src/js/entries/demo3.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import {GreetingReducer} from '../reducers/GreetingReducer.js'
import GreetingApp from '../components/GreetingApp.js'

let store = createStore(GreetingReducer)

render(
  <Provider store={store}>
    <GreetingApp />
  </Provider>,
  document.getElementById('reactHolder')
)

这里用到了reduxProvider,它会把store附属到组件树的context上,其子组件就都可以访问到store了。

本篇源代码地址