本篇使用redux结合react重写刚才那个很简单的hello world示例。
redux有三个重要的理念:单一数据源、状态是只读的、使用纯函数转换状态。具体见链接
1 | npm install redux react-redux --save |
|---|
web-src/js/components/GreetingConstant.js
1 | 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。
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 Components与Container Components,有的地方叫Dump Components与Smart 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,这里一般会用到redux的connect方法,还是看下面的代码。
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 GreetingContainerweb-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')
)这里用到了redux的Provider,它会把store附属到组件树的context上,其子组件就都可以访问到store了。
本篇源代码地址