<div id="counter">
<p id="counter-value">0</p>
<button id="increment-btn">+</button>
<button id="decrement-btn">-</button>
</div>
import {createStore} from 'redux';
let counterValue = document.getElementById('counter-value');
let incrementBtn = document.getElementById('increment-btn');
let decrementBtn = document.getElementById('decrement-btn');
const INCREMENT='INCREMENT';
const DECREMENT = 'DECREMENT';
let initState = 0;
function reducer(state=initState,action){
switch(action.type){
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
}
let store=createStore(reducer);
function render() {
counterValue.innerHTML=store.getState();
}
store.subscribe(render);
render();
incrementBtn.addEventListener('click',function () {
store.dispatch({type:INCREMENT});
});
decrementBtn.addEventListener('click',function () {
store.dispatch({type:DECREMENT});
});
src\redux\index.js
import createStore from './createStore'
export {
createStore
}
src\redux\createStore.js
export default function createStore(reducer, preloadedState) {
let currentState = preloadedState;
let currentListeners = [];
function getState() {
return currentState;
}
function subscribe(listener) {
currentListeners.push(listener);
return function unsubscribe() {
const index = currentListeners.indexOf(listener);
currentListeners.splice(index, 1);
};
}
function dispatch(action) {
if (Object.getPrototypeOf(action) !== Object.prototype) {
throw new Error(`动作必须是一个纯对象,如果想进行异步操作请使用中间件`);
}
if (typeof action.type === "undefined") {
throw new Error(`动作不能一个值为undefined的type属性`);
}
currentState = reducer(currentState, action);
for (let i = 0; i < currentListeners.length; i++) {
const listener = currentListeners[i];
listener();
}
return action;
}
dispatch({ type:'@@redux/INIT' });
return {
dispatch,
subscribe,
getState
};
}
import React, { Component } from 'react';
import { createStore } from '../redux';
function reducer(state=0,action){
switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
const store = createStore(reducer,0);
export default class Counter extends Component {
constructor(props) {
super(props);
this.state = { value: 0 };
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => this.setState({ value: store.getState() }));
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<div>
<p>{this.state.value}</p>
<button onClick={() => store.dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => store.dispatch({ type: 'DECREMENT' })}>-</button>
<button onClick={
() => {
setTimeout(() => {
store.dispatch({ type: 'INCREMENT' })
}, 1000);
}
}>1秒后加1</button>
</div>
)
}
}
import React, { Component } from 'react';
import { createStore,bindActionCreators} from '../redux';
function reducer(state=0,action){
switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
const store = createStore(reducer,0);
function increment(){
return {type:'INCREMENT'};
}
function decrement(){
return {type:'DECREMENT'};
}
const actions = {increment,decrement};
//const boundIncrement = bindActionCreators(increment,store.dispatch);//可以传一个函数
const boundActions = bindActionCreators(actions,store.dispatch);//也可以传对象
export default class Counter extends Component {
constructor(props) {
super(props);
this.state = { value: 0 };
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => this.setState({ value: store.getState() }));
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<div>
<p>{this.state.value}</p>
<button onClick={boundIncrement}>+</button>
<button onClick={boundIncrement}>-</button>
</div>
)
}
}
bindActionCreators.js
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
import React from 'react';
import ReactDOM from 'react-dom';
import Counter1 from './components/Counter1';
import Counter2 from './components/Counter2';
ReactDOM.render(<><Counter1/><hr/><Counter2/></>,document.getElementById('root'));
src\redux\index.js
import createStore from './createStore'
import bindActionCreators from './bindActionCreators'
import combineReducers from './combineReducers'
export {
createStore,
bindActionCreators,
combineReducers
}
src\redux\combineReducers.js
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
return function combination(state = {}, action) {
const nextState = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
const reducer = reducers[key];
const previousStateForKey = state[key];
const nextStateForKey = reducer(previousStateForKey, action);
nextState[key] = nextStateForKey;
}
return nextState;
}
}
src\store\index.js
import { createStore } from '../redux';
import reducer from './reducers';
const store = createStore(reducer,{counter1:0,counter2:0});
export default store ;
src\store\action-types.js
export const INCREMENT1 = 'INCREMENT1';
export const DECREMENT1 = 'DECREMENT1';
export const INCREMENT2 = 'INCREMENT2';
export const DECREMENT2 = 'DECREMENT2';
src\store\actions\counter1.js
import * as types from '../action-types';
export default {
increment1(){
return {type:types.INCREMENT1};
},
decrement1(){
return {type:types.DECREMENT1};
}
}
src\store\actions\counter2.js
import * as types from '../action-types';
export default {
increment2(){
return {type:types.INCREMENT2};
},
decrement2(){
return {type:types.DECREMENT2};
}
}
src\store\reducers\index.js
import {combineReducers} from '../../redux';
import counter1 from './counter1';
import counter2 from './counter2';
export default combineReducers({
counter1,
counter2
});
src/store/reducers/counter1.js
import * as types from '../action-types';
export default function (state=0,action){
switch(action.type){
case types.INCREMENT1:
return state + 1;
case types.DECREMENT1:
return state - 1;
default:
return state;
}
}
src/store/reducers/counter2.js
import * as types from '../action-types';
export default function (state=0,action){
switch(action.type){
case types.INCREMENT2:
return state + 1;
case types.DECREMENT2:
return state - 1;
default:
return state;
}
}
src\components\Counter1.js
import React, { Component } from 'react';
import actions from '../store/actions/counter1';
import store from '../store';
import {bindActionCreators} from '../redux';
const boundActions = bindActionCreators(actions,store.dispatch);
export default class Counter extends Component {
constructor(props) {
super(props);
this.state = {value:0}
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => this.setState({ value: store.getState().counter1 }));
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<div>
<p>{this.state.value}</p>
<button onClick={boundActions.increment1}>+</button>
<button onClick={boundActions.decrement1}>-</button>
</div>
)
}
}
src\components\Counter2.js
import React, { Component } from 'react';
import actions from '../store/actions/counter2';
import store from '../store';
import {bindActionCreators} from '../redux';
const boundActions = bindActionCreators(actions,store.dispatch);
export default class Counter extends Component {
constructor(props) {
super(props);
this.state = {value:0}
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => this.setState({ value: store.getState().counter2 }));
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<div>
<p>{this.state.value}</p>
<button onClick={boundActions.increment2}>+</button>
<button onClick={boundActions.decrement2}>-</button>
</div>
)
}
}
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Counter1 from './components/Counter1';
import Counter2 from './components/Counter2';
import store from './store';
import {Provider} from './react-redux';
ReactDOM.render(<Provider store={store}><Counter1/><hr/><Counter2/></Provider>,document.getElementById('root'));
src/components/Counter.js
import React, { Component } from 'react';
import actions from '../store/actions/counter1';
import {connect} from '../react-redux'
class Counter extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>{this.props.value}</p>
<button onClick={this.props.increment}>+</button>
<button onClick={this.props.decrement}>-</button>
</div>
)
}
}
let mapStateToProps = state=>({value:state.counter});
export default connect(
mapStateToProps,
actions
)(Counter)
src\react-redux\index.js react-redux
import Provider from './Provider';
import connect from './connect';
export {
Provider,
connect
}
src\react-redux\Provider.js Provider.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { ReactReduxContext } from './Context'
export default class Provider extends Component {
static propTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
}),
children: PropTypes.any
}
constructor(props) {
super(props)
}
render() {
return (
<ReactReduxContext.Provider value={{store:this.props.store}}>
{this.props.children}
</ReactReduxContext.Provider>
)
}
}
src\react-redux\connect.js connect.js
import React from "react";
import { bindActionCreators } from "../redux";
import { ReactReduxContext } from "./Context";
export default function(mapStateToProps, mapDispatchToProps) {
return function wrapWithConnect(WrappedComponent) {
return class extends React.Component {
static contextType = ReactReduxContext;
constructor(props, context) {
super(props);
this.state = mapStateToProps(context.store.getState());
}
componentDidMount() {
this.unsubscribe = this.context.store.subscribe(() =>
this.setState(mapStateToProps(this.context.store.getState()))
);
}
shouldComponentUpdate() {
if (this.state === mapStateToProps(this.context.store.getState())) {
return false;
}
return true;
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
let actions = bindActionCreators(
mapDispatchToProps,
this.context.store.dispatch
);
return <WrappedComponent {...this.state} {...actions} />;
}
};
};
}
src\react-redux\Context.js Context.js
import React from 'react'
export const ReactReduxContext = React.createContext(null)
export default ReactReduxContext
src\react-redux-old\Provider.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Provider extends Component {
static propTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
}),
children: PropTypes.any
}
constructor(props) {
super(props);
}
static childContextTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
})
}
getChildContext(){
return {store:this.props.store};
}
render() {
return this.props.children
}
}
src\react-redux-old\connect.js
import React from 'react';
import {bindActionCreators} from '../redux';
import PropTypes from 'prop-types';
export default function(mapStateToProps,mapDispatchToProps){
return function wrapWithConnect(WrappedComponent) {
return class extends React.Component{
constructor(props,context){
super(props);
this.state = mapStateToProps(context.store.getState());
}
static contextTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
})
}
componentDidMount(){
this.unsubscribe = this.context.store.subscribe(
()=>this.setState(mapStateToProps(this.context.store.getState()))
);
}
componentWillUnmount(){
this.unsubscribe();
}
render(){
let actions = bindActionCreators(mapDispatchToProps,this.context.store.dispatch);
return <WrappedComponent {...this.state} {...actions}/>
}
}
}
}