1 运行效果*:点击加减,总数也同步更新
2代码
2.1 \src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import ControlPanel from './views/ControlPanel';
import './index.css';
import store from './Store.js';
import Provider from './Provider.js';
ReactDOM.render(
<Provider store={store}>
<ControlPanel />
</Provider>,
document.getElementById('root')
);
2.2 \views\ControlPanel.js
import React, { Component } from 'react';
import Counter from './Counter.js';
import Summary from './Summary.js';
const style = {
margin: '20px'
};
class ControlPanel extends Component {
render() {
return (
<div style={style}>
<Counter caption="First" />
<Counter caption="Second" />
<Counter caption="Third" />
<hr/>
<Summary />
</div>
);
}
}
export default ControlPanel;
2.3 \views\Counter.js
import React, { Component, PropTypes } from 'react';
import * as Actions from '../Actions.js';
const buttonStyle = {
margin: '10px'
};
class Counter extends Component {
render() {
const {caption, onIncrement, onDecrement, value} = this.props;
return (
<div>
<button style={buttonStyle} onClick={onIncrement}>+</button>
<button style={buttonStyle} onClick={onDecrement}>-</button>
<span>{caption} count: {value}</span>
</div>
);
}
}
Counter.propTypes = {
caption: PropTypes.string.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired,
value: PropTypes.number.isRequired
};
class CounterContainer extends Component {
constructor(props, context) {
super(props, context);
this.onIncrement = this.onIncrement.bind(this);
this.onDecrement = this.onDecrement.bind(this);
this.onChange = this.onChange.bind(this);
this.getOwnState = this.getOwnState.bind(this);
this.state = this.getOwnState();
}
getOwnState() {
return {
value: this.context.store.getState()[this.props.caption]
};
}
onIncrement() {
this.context.store.dispatch(Actions.increment(this.props.caption));
}
onDecrement() {
this.context.store.dispatch(Actions.decrement(this.props.caption));
}
onChange() {
this.setState(this.getOwnState());
}
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.caption !== this.props.caption) ||
(nextState.value !== this.state.value);
}
componentDidMount() {
this.context.store.subscribe(this.onChange);
}
componentWillUnmount() {
this.context.store.unsubscribe(this.onChange);
}
render() {
return <Counter caption={this.props.caption}
onIncrement={this.onIncrement}
onDecrement={this.onDecrement}
value={this.state.value} />
}
}
CounterContainer.propTypes = {
caption: PropTypes.string.isRequired
};
CounterContainer.contextTypes = {
store: PropTypes.object
}
export default CounterContainer;
2.4 \src\ActionTypes.js
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';
2.5 \src\Actions.js
import * as ActionTypes from './ActionTypes.js';
export const increment = (counterCaption) => {
return {
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
};
};
export const decrement = (counterCaption) => {
return {
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
};
};
2.6 \src\Store.js
import {createStore} from 'redux';
import reducer from './Reducer.js';
const initValues = {
'First': 0,
'Second': 10,
'Third': 20
};
const store = createStore(reducer, initValues);
export default store;
2.7 \src\Reducer.js
import * as ActionTypes from './ActionTypes.js';
export default (state, action) => {
const {counterCaption} = action;
switch (action.type) {
case ActionTypes.INCREMENT:
return {...state, [counterCaption]: state[counterCaption] + 1};
case ActionTypes.DECREMENT:
return {...state, [counterCaption]: state[counterCaption] - 1};
default:
return state
}
}
2.8 \src\views\Summary.js
import React, { Component, PropTypes } from 'react';
class Summary extends Component {
render() {
const sum = this.props.sum;
return (
<div>Total Count: {sum}</div>
);
}
}
Summary.propTypes = {
sum: PropTypes.number.isRequired
};
class SummaryContainer extends Component {
constructor(props, context) {
super(props, context);
this.onChange = this.onChange.bind(this);
this.state = this.getOwnState();
}
onChange() {
this.setState(this.getOwnState());
}
getOwnState() {
const state = this.context.store.getState();
let sum = 0;
for (const key in state) {
if (state.hasOwnProperty(key)) {
sum += state[key];
}
}
return { sum: sum };
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.sum !== this.state.sum;
}
componentDidMount() {
this.context.store.subscribe(this.onChange);
}
componentWillUnmount() {
this.context.store.unsubscribe(this.onChange);
}
render() {
const sum = this.state.sum;
return (
<Summary sum={sum} />
);
}
}
SummaryContainer.contextTypes = {
store: PropTypes.object
}
export default SummaryContainer;