本篇使用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 ComponentsContainer Components
PurposeHow things look (markup, styles)How things work (data fetching, state updates)
Aware of ReduxNoYes
To read dataRead data from propsSubscribe to Redux state
To change dataInvoke callbacks from propsDispatch Redux actions
Are writtenBy handUsually 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了。

本篇源代码地址