此书讲得蛮详细,
从Flux一步一步过渡到Redux。
写过的代码舍不得扔,
立此存照吧。
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import ControlPanel from './views/ControlPanel';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<ControlPanel />, document.getElementById('root'));
registerServiceWorker();
AppDispatcher.js
import {Dispatcher} from 'flux';
export default new Dispatcher();
ActionTypes.js
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';
Actions.js
import * as ActionTypes from './ActionTypes';
import AppDispatcher from './AppDispatcher';
export const increment = (counterCaption) => {
AppDispatcher.dispatch({
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
});
};
export const decrement = (counterCaption) => {
AppDispatcher.dispatch({
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
});
};
CounterStore.js
import AppDispatcher from '../AppDispatcher';
import * as ActionTypes from '../ActionTypes';
import {EventEmitter} from 'events';
const CHANGE_EVENT = 'changed';
const counterValues = {
'First': 0,
'Second': 10,
'Third': 30
};
const CounterStore = Object.assign({}, EventEmitter.prototype, {
getCounterValues: function() {
return counterValues;
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
addChangeListener:function(callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
});
CounterStore.dispatchToken = AppDispatcher.register((action)=> {
if (action.type === ActionTypes.INCREMENT) {
counterValues[action.counterCaption]++;
CounterStore.emitChange();
} else if (action.type === ActionTypes.DECREMENT) {
counterValues[action.counterCaption]--;
CounterStore.emitChange();
}
});
export default CounterStore;
SummaryStore.js
import AppDispatcher from '../AppDispatcher';
import * as ActionTypes from '../ActionTypes';
import CounterStore from './CounterStore';
import {EventEmitter} from 'events';
const CHANGE_EVENT = 'changed';
function computerSummary(counterValues) {
let summary = 0;
for (const key in counterValues) {
if (counterValues.hasOwnProperty(key)) {
summary += counterValues[key];
}
}
return summary;
}
const SummaryStore = Object.assign({}, EventEmitter.prototype, {
getSummary: function() {
return computerSummary(CounterStore.getCounterValues());
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
});
SummaryStore.dispatchToken = AppDispatcher.register((action) => {
if ((action.type === ActionTypes.INCREMENT) ||
(action.type === ActionTypes.DECREMENT)) {
AppDispatcher.waitFor([CounterStore.dispatchToken]);
SummaryStore.emitChange();
}
});
export default SummaryStore;
CounterPanel.js
import React, { Component } from 'react';
import Counter from './Counter';
import Summary from './Summary';
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;
Counter.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import * as Actions from '../Actions.js';
import CounterStore from '../stores/CounterStore.js';
const buttonStyle = {
margin: '10px'
};
const propTypes = {
caption: PropTypes.string.isRequired
};
class Counter extends Component {
constructor(props) {
super(props);
this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
this.onClickDecrementButton = this.onClickDecrementButton.bind(this);
this.onChange = this.onChange.bind(this);
this.state = {
count: CounterStore.getCounterValues()[props.caption]
}
}
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.caption !== this.props.caption) ||
(nextState !== this.state.count);
}
componentDidMount() {
CounterStore.addChangeListener(this.onChange);
}
componentWillUnmout() {
CounterStore.removeChangeListener(this.onChange);
}
onChange() {
const newCount = CounterStore.getCounterValues()[this.props.caption];
this.setState({count: newCount});
}
onClickIncrementButton() {
Actions.increment(this.props.caption);
}
onClickDecrementButton() {
Actions.decrement(this.props.caption);
}
render() {
const {caption} = this.props;
return (
<div>
<button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
<button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
<span> { caption } count: {this.state.count}</span>
</div>
);
}
}
Counter.defaultProps = {
initValue: 0,
onUpdate: f => f
};
Counter.propTypes = propTypes
export default Counter;
Summary.js
import React, {Component} from 'react';
import SummaryStore from '../stores/SummaryStore';
class Summary extends Component {
constructor(props) {
super(props);
this.onUpdate = this.onUpdate.bind(this);
this.state = {
sum: SummaryStore.getSummary()
}
}
componentDidMount() {
SummaryStore.addChangeListener(this.onUpdate);
}
componentWillUnmount() {
SummaryStore.removeChangeListener(this.onUpdate);
}
onUpdate() {
this.setState({
sum: SummaryStore.getSummary()
})
}
render() {
return (
<div>Total Count: {this.state.sum}</div>
);
}
}
export default Summary;