Redux Flow
一、介绍
(一)、redux:
1、reducer念:
reducer就是一个纯函数,接收旧的state和action,返回新的state。
(previousState,action)=>newState;
名字由来:Array.prototype.reduce(reducer,initValue)里的回调函数属于相同的类型。
reduce解释:
https://www.runoob.com/jsref/jsref-reduce.html
Redux是JaveScript应用的状态容器。
安装:npm install redux –save
(二)、react-redux
每次都重新调用render和getState太麻烦了,so有了react-redux支持
安装: npm install react-redux –save
提供了两个api:
1、Provider 为后代组件提供store
2、connect 为组件提供数据和变更方法
(三)、异步
Redux只是纯粹的状态管理器,默认只支持同步,实现异步,如延迟,网络请求,就需要中间件的支持:
1、安装:npm install redux-thunk redux-logger –save
2、应用:
store.js
import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
const store = createStore(fruitReducer,applyMiddleware(logger,thunk));
注:若出现错误类似/babel-preset-reactapp/node_modules/@babel/runtime/helpers/slicedToArray at webpackMissingModule ‘,
就 安装: npm add @babel/runtime
redux原理
- 存储状态state
- 获取状态getstate
- 更新状态dispatch
补充:redux的api中createStore()
接收一个参数:存储状态和方法
接收两个参数:1、存储状态 2、存储方法(处理异步方法)
二、代码实现
下面从零开始搭建项目,手写redux及react-redux源码:
(一)、搭建项目:
- 安装官⽅脚⼿架:npm install -g create-react-app
- 创建项⽬:create-react-app lesson1
- 启动项⽬:npm start
(二)、创建页面:
1、在src(源码)新建文件夹,名为:pages
在pages新建文件:MyReactReduxPage.js(用来查看实现redux的效果展示)
import React, { Component } from "react";
import { connect } from "../myReact-redux";
import { add, minus, asyAdd } from '../action/reactReduxPage';
class MyReactReduxPage extends Component {
render() {
console.log("props", this.props);
const { counter, add, asyAdd, minus } = this.props;
return (
<div>
<h1>MyReactReduxPage</h1>
<p>counter:{counter}</p>
<button onClick={add}>add</button>
<button onClick={asyAdd}>asyAdd</button>
<button onClick={minus}>minus</button>
</div>
);
}
}
const mapStateToProps = state => {
return {
counter: state,
}
}
const mapDispatchToProps = {
add,
minus,
asyAdd
}
//connect中的参数:state映射和事件映射
export default connect(
mapStateToProps,//状态映射
mapDispatchToProps,//派发事件映射
)(MyReactReduxPage);
2、新建action文件夹,用于管理dispatch方法:
./action/reactReduxPage.js
export const add = () => {
return { type: 'add' }
};
export const minus = () => {
return { type: 'minus' }
};
//add,minus 没有写dispatch,但是redux最终也是给它执行了dispatch
//dispatch 是因为引入了中间件,再去调用一次dispatch,实现异步
export const asyAdd = () => dispatch => {
setTimeout(() => {
dispatch({
type: "add",
});
}, 1000);
}
3、myReact-redux.js
// import React from 'react'
import PropTypes from 'prop-types'
import { bindActionCreators } from './kRedux';
//2、用hooks实现(就是用方法组件)
import React, { useContext, useState, useEffect } from 'react'
const Context = React.createContext();
/*
mapStateToProps 接收一个函数作为参数,直接返回一个函数state;
mapDispatchToProps 是一个对象,所以设置它的初始值为{}
connect 是一个高阶函数,参数是组件,所以要return返回一个组件
*/
export const connect = (mapStateToProps = state => state, mapDispatchToProps = {})=> (WarpComponent) => {
return class ConnectComponent extends React.Component {
//class 组件中声明静态的contentTypes可以获取上下文Context
static contextTypes = {
store: PropTypes.object
}
constructor(props, context) {
super(props, context)
this.state = {
props: {}
}
}
componentDidMount() {
const { store } = this.context
store.subscribe(() => this.update())
this.update()
}
//更新state值得方法
update() {
const { store } = this.context
//state => ({num:state.counter})
const stateProps = mapStateToProps(store.getState())
//{add:()=>({type:'add'})}
// {add:(...args)d=>ispatchEvent(creator(...args))}
const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch)
this.setState({
props: {
...this.state.props,//当前值
...stateProps,//num:state.counter
...dispatchProps //add:(...args)=>dispatch(creator(...args))
}
})
}
render() {
return <WarpComponent {...this.state.props}></WarpComponent>
}
}
}
export class Provider extends React.Component {
static childContextTypes = {
store: PropTypes.object
}
//class 组件提供的方法 getChildContext
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render() {
return this.props.children
}
}
//2、用hooks实现(就是用方法组件) --start
export function Provider2(props) {
return (
<Context.Provider value={props.store}>
{props.children}
</Context.Provider>
)
}
export const connect2 = (
mapStateToProps = state => state,
mapDispatchToProps = {},
) => Cmp => {
return () => {
const store = useContext(Context);
const getProps = () => {
const stateProps = mapStateToProps(store.getState());
const dispatchProps = bindActionCreators(
mapDispatchToProps,
store.dispatch,
);
return {
...stateProps,
...dispatchProps,
}
}
/*
hooks 里 的1、useState暴露两个参数[state,setState],
其中:state--获取值,setstate去设置值
2、useEffect 设置state的方法,相当于class 里的setState
*/
const [props, setProps] = useState({ ...getProps() });
useEffect(() => {
store.subscribe(() => {
setProps({ ...props, ...getProps() });
})
})
return <Cmp {...props}/>
}
}
// 用hooks实现(就是用方法组件) --end
4、myRedux.js
提供了createStore,applyMiddleware(中间件),compose,bindActionCreators方法
export function createStore(reducer, enhancer) {
//enhancer增强器
if (enhancer) {
return enhancer(createStore)(reducer)
}
//保存状态:
let currentState = undefined;
//回调函数:
let currentListeners = [];
function getState() {
return currentState
}
function subscribe(listener) {
currentListeners.push(listener)
}
function dispatch(action) {
currentState = reducer(currentState, action)
currentListeners.forEach((v) => v())
return action;
}
dispatch({ type: '@IMOOC/KKB-REDUX' }) //默认执行一遍,才能获取state初始值
return { getState, subscribe, dispatch }
}
//中间件目的是为了实现异步调用
export function applyMiddleware(...middlewares) {
//返回强化以后的函数
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = store.dispatch
const midApi = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
//使用中间件可以获取状态值、派发action (补充:获取值,action执行方法dispatch更新值)
const middlewareChain = middlewares.map(middleware => middleware(midApi))
//compose 可以middlewareChain函数数组合并成一个函数
dispatch = compose(...middlewareChain)(store.dispatch)
//返回store,其中dispatch重新修改了,需要进行覆盖store.dispatch的旧值
return {
...store,
dispatch
}
}
}
//compose实现函数的聚合,它返回的是一个函数
export function compose(...funcs) {
if (funcs.length == 0) {
return arg => arg //传入的函数个数为0,就直接返回一个空的函数
}
if (funcs.length == 1) {
return funcs[0]
}
return funcs.reduce((left, right) => (...args) => right(left(...args)))
}
function bindActionCreator(creator, dispatch) {
return (...args) => dispatch(creator(...args))
}
export function bindActionCreators(creators, dispatch) {
return Object.keys(creators).reduce((ret, item) => {
ret[item] = bindActionCreator(creators[item], dispatch)
return ret
}, {})
}
//另一种写法
export function bindActionCreators2(creators, dispatch) {
return Object.keys(creators).reduceRight((ret, item) => {
ret[item] = bindActionCreator(creators[item], dispatch);
return ret;
}, {})
}
5、react-redux状态管理:
新建store文件夹:
1)状态值管理: counterReducer.js
export const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'add':
return state + 1;
case 'minus':
return state - 1;
default:
return state;
}
}
2)提供中间件方法:thunk,logger
store/myReactReduxStore.js
// import { createStore } from "../kReact-redux";
import { applyMiddleware, createStore } from '../kRedux'
import { counterReducer } from './counterReducer'
//实现异步调用:回调函数重新调用自己
function logger({ dispatch, getState }) {
return dispatch => action => {
//中间件任务
console.log(action.type + '执行了!!!');
//下一个中间件
return dispatch(action);
}
}
const thunk = ({ dispatch, getState }) => dispatch => action => {
//如果是方法的话:dispatch经过一套工序后在调用,实现异步
if (typeof action == 'function') {
return action(dispatch, getState)
}
//不是方法就直接调用
return dispatch(action)
}
// const store = createStore(counterReducer);
const store = createStore(counterReducer, applyMiddleware(logger, thunk));
export default store;
6、测试效果:
1)src/index.js
Provider 是react提供全局的存储值
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from './kReact-redux';
import store from './store/myReactReduxStore'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
serviceWorker.unregister();
2)App.js引入MyReactReduxPage组件
完毕!