redux 和 react-redux两个不同来讲的。
redux 就是一个状态的维护。
react-redux 则表示的是provider + ReactReduxContext + connect + context + redux
ps: ReactReduxContext 在 connect.js 和Context.js Provider.js 中都有使用
ps: react-redux会导出两个内容,一个Provider 另一个是 connect。
这两个重载之间没有任何分号之类的
interface 接口定义方法的话,是:
type 定义方法的 话,用=> 隔开。
必选参数不能放在可选参数的后边。 就是这里不能写= AnyAction的默认值,要不第二个参数不知道算谁的了。
这两种写法一样的
定义在哪里,在哪里使用的时候 传
bindActionCreators
使用的时候就是, onClick = { boundActions.increment }
??? extends中的继承又=是什么鬼。
父类引用子类,但是子类不能使用 父类的引用
connect-router-redux
为了保证全局history的唯一
外边引进来的CounterState的 另换个名字type Props = CounterState;
actions
打印,打印,打印,打印,打印,打印,打印,打印,打印
redux 和 react-redux两个不同来讲的。
redux 就是一个状态的维护。
react-redux 则表示的是provider + ReactReduxContext + connect + context + redux
ps: ReactReduxContext 在 connect.js 和Context.js Provider.js 中都有使用
ps: react-redux会导出两个内容,一个Provider 另一个是 connect。
react-redux
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)
8.3 react-redux\index.js
import Provider from './Provider';
import connect from './connect';
export {
Provider,
connect
}
8.4 react-redux\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>
)
}
}
8.5 react-redux\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} />;
}
};
};
}
8.6 react-redux\Context.js
import React from 'react'
export const ReactReduxContext = React.createContext(null)
export default ReactReduxContext
bindActionCreators.js
Counter.js
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>
)
}
}
6.2 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
}
7.3 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;
}
}
compose
function add1(str){
return '1'+str;
}
function add2(str){
return '2'+str;
}
function add3(str){
return '3'+str;
}
function compose(...funcs){
return funcs.reduce((a,b)=>(...args)=>a(b(...args)));
}
// 第一次返回 add3(add2(...args))
// 第二次返回 add3(add2(add1(...args)))
// 第三次返回 返回执行的结果
let result = compose(add3,add2,add1)('zhufeng');
console.log(result);
redux中间件
store\index.tsx
import { createStore, Store, AnyAction, Middleware, MiddlewareAPI, StoreEnhancer, StoreEnhancerStoreCreator, applyMiddleware } from '../redux';
import reducer from './reducers';
import { CombinedState } from './reducers';
//const store: Store<CombinedState, AnyAction> = createStore<CombinedState, AnyAction, {}, {}>(reducer, { counter1: { number: 0 }, counter2: { number: 0 } });
let logger: Middleware = (api: MiddlewareAPI) => (next: any) => (action: any) => {
console.log(api.getState());
next(action);
console.log(api.getState());
return action;
};
+let thunk: Middleware = (api: MiddlewareAPI) => (next: any) => (action: any) => {
+ if (typeof action == 'function') {
+ return action(api.dispatch, api.getState);
+ }
+ return next(action);
+};
+function isPromise(obj: any) {
+ return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
+}
+let promise: Middleware = (api: MiddlewareAPI) => (next: any) => (action: any) => {
+ return isPromise(action.payload)
+ ? action.payload
+ .then((result: any) => api.dispatch({ ...action, payload: result }))
+ .catch((error: any) => {
+ api.dispatch({ ...action, payload: error, error: true });
+ return Promise.reject(error);
+ })
+ : next(action);
+};
+let storeEnhancer: StoreEnhancer = applyMiddleware(thunk, promise, logger);
let storeEnhancerStoreCreator: StoreEnhancerStoreCreator = storeEnhancer(createStore);
let store: Store = storeEnhancerStoreCreator(reducer);
export default store;
3.5 applyMiddleware.tsx
import compose from './compose';
import { Middleware, Store, StoreEnhancer, Dispatch, MiddlewareAPI, StoreCreator, Action, AnyAction, Reducer } from './'
export function applyMiddleware(
...middlewares: Middleware[]
): StoreEnhancer
export default function applyMiddleware(
...middlewares: Middleware[]
): StoreEnhancer {
return (createStore: StoreCreator) => <S, A extends Action>(
reducer: Reducer<S, A>
): Store<S, A> => {
const store = createStore(reducer)
let dispatch: Dispatch;
const middlewareAPI: MiddlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
actions\counter1.tsx
+import { AnyAction, Dispatch } from '../../redux';
const actions = {
increment1(): AnyAction {
return { type: types.INCREMENT1 };
},
+ increment1Async() {
+ return function (dispatch: Dispatch) {
+ setTimeout(() => {
+ dispatch({ type: types.INCREMENT1 });
+ }, 1000);
+ }
+ },
+ increment1Promise() {
+ return {
+ type: types.INCREMENT1,
+ payload: new Promise((resolve: any, reject: any) => {
+ setTimeout(() => {
+ let result = Math.random();
+ if (result > .5) {
+ resolve(result);
+ } else {
+ reject(result);
+ }
+ }, 1000);
+ })
+ }
+ },
decrement1(): AnyAction {
return { type: types.DECREMENT1 };
}
}
export default actions;
Counter1.tsx
import React, { Component } from 'react';
import actions from '../store/actions/counter1';
import { CombinedState } from '../store/reducers';
import { Counter1State } from '../store/reducers/counter1';
import { connect } from '../react-redux';
+import { ActionCreatorsMapObject, AnyAction } from '../redux';
type Props = Counter1State & typeof actions;
class Counter1 extends Component<Props> {
render() {
let { number, increment1, decrement1 } = this.props;
return (
<div>
<p>{number}</p>
<button onClick={increment1}>+</button>
+ <button onClick={this.props.increment1Async}>异步+1</button>
+ <button onClick={this.props.increment1Promise}>promise异步+1</button>
</div>
)
}
}
let mapStateToProps = (state: CombinedState): Counter1State => state.counter1;
+export default connect<any>(
mapStateToProps,
actions
)(Counter1
connect-react-router 只学会使用,不考虑原理
2.9 Counter.tsx
Home.tsx
<button onClick={() => this.props.history.go(-1)}>返回</button>
Counter.tsx
import React, { Component } from 'react'
import { connect } from 'react-redux';
import actions from '../store/actions/counter';
import { CounterState } from '../store/reducers/counter';
import { RootState } from '../store/reducers';
type Props = CounterState & typeof actions;
class Counter extends Component<Props> {
render() {
return (
<>
<p>{this.props.number}</p>
<button onClick={this.props.increment}>+</button>
<button onClick={this.props.decrement}>-</button>
<button onClick={() => this.props.go('/')}>Home</button>
</>
)
}
}
let mapStateToProps = (state: RootState): CounterState => state.counter;
export default connect(
mapStateToProps,
actions
)(Counter);
actions
import * as types from '../action-types';
import { push } from '../../connected-react-router';
export default {
increment() {
return { type: types.INCREMENT }
},
decrement() {
return { type: types.DECREMENT }
},
go(path: string) {
return push(path);
}
}
reducers\counter.tsx
import { AnyAction } from 'redux';
export interface CounterState {
number: number
}
let initialState: CounterState = { number: 0 }
export default function (state: CounterState = initialState, action: AnyAction): CounterState {
switch (action.type) {
case types.INCREMENT:
return { number: state.number + 1 };
case types.DECREMENT:
return { number: state.number - 1 };
default:
return state;
}
}
src\history.tsx
import { createHashHistory } from 'history'
let history = createHashHistory();
export default history;
store\index.tsx
import { applyMiddleware, createStore } from 'redux'
import { routerMiddleware } from '../connected-react-router'
import history from '../history';
import reducers from './reducers';
const store = applyMiddleware(routerMiddleware(history))(createStore)(reducers);
export default store;
src\index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Route, Link } from 'react-router-dom';
import Home from './components/Home';
import Counter from './components/Counter';
import { ConnectedRouter } from './connected-react-router'
import history from './history';
import store from './store';
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<>
<Link to="/">Home</Link>
<Link to="/counter">Counter</Link>
<Route exact={true} path="/" component={Home} />
<Route path="/counter" component={Counter} />
</>
</ConnectedRouter>
</Provider>
, document.getElementById('root'));