三、React全家桶(一)
Reducer
什么是Reducer?
reducer就是一个纯函数,接收旧的 state 和 action,返回新的state
之所以将这样的函数称之为 reducer,是因为这种函数与被传入 Array.prototype.reduce(reducer, ?initialValue) 里的回调函数属于相同的类型。保持 reducer 纯净非常重要。永远不要在 reducer里做这些操作:
- 修改传入的参数
- 执行有副作用的操作,如API请求或路由跳转
- 调用非纯函数,如 Date.now() 或 Math.random()
什么是reduce
const array1 = [1, 2, 3, 4]; // 类似累加:accumulator为total,currentValue为当前value const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // expected output: 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // expected output: 15
函数聚合
function f1() { console.log("f1"); } function f2() { console.log("f2"); } function f3() { console.log("f3"); }
现在想输出 : f1 f2 f3
方法1:f1();f2();f3();
方法2:f3(f2(f1()))
方法3:
function compose(...funcs){ const len = funcs.length if(len === 0) return arg => arg if(len === 1) return funcs[0] return funcs.reduce((left, right)=>(...args)=>right(left(...args))) } compose(f1,f2,f3)()
Redux上手
import {createStore} from 'redux'; function counterReducer(state=0, action){ // console.log(state); switch (action.type){ case "add": return state + 1 default: return state } } const store = createStore(counterReducer) export default store
创建 ReduxPage
import React, {Component} from 'react' import store from '../store/reduxStore'; export default class ReduxPage extends Component { componentDidMount(){ store.subscribe(()=>{ // console.log('subscribe'); // this.forceUpdate() this.setState({}) }) } render() { // console.log('ReduxPage', store); return ( <div> <h1>ReduxPage</h1> <p>counter:{store.getState()}</p> <button onClick={() => store.dispatch({type: 'add'})}>add</button> </div> ) } }
检查点
1、createStore 创建 store
2、reducer 初始化、修改状态函数
3、getState 获取状态值
4、dispatch 提交更新
5、subscribe 变更订阅
react-redux
每次都重新调用 render 和 getState 太low了,想用更react的方式来写,需要react-redux的支持
npm install react-redux --save
提供了两个api
1、Provider 为后代组件提供store
2、connect 为组件提供数据和变更方法
全局提供store,index.js
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import store from './store/reactReduxStore'; import {Provider} from 'react-redux'; ReactDOM.render( <Provider store={store}> <App /> </Provider> , document.getElementById('root'));
reactReduxStore.js
import {createStore} from 'redux'; function counterReducer(state=0, action){ // console.log(state); switch (action.type){ case "add": return state + 1 case "minus": return state - 1 default: return state } } const store = createStore(counterReducer) export default store
ReactReduxPage.js
import React, { Component } from 'react' import {connect} from 'react-redux'; class ReactReduxPage extends Component { render() { console.log(this.props); const {counter, add, minus} = this.props return ( <div> <h1>ReactReduxPage</h1> <p>counter:{counter}</p> <button onClick={add}>add</button> <button onClick={minus}>minus</button> </div> ) } } export default connect( // mapStateToProps state=>{ return {counter: state} }, // mapDispatchToProps { add: ()=>{ return { type: 'add' } }, minus: ()=>{ return { type: 'minus' } } } )(ReactReduxPage)
异步
Redux只是个纯粹的状态管理器,默认只支持同步,实现异步任务 比如延迟、网络请求,需要中间件的支持,比如我们试用最简单的redux-thunk和redux-logger
npm install redux-thunk redux-logger --save
应用中间件,store.js
import {createStore, applyMiddleware} from 'redux'; import logger from "redux-logger" import thunk from "redux-thunk"; const store = createStore(counterReducer, applyMiddleware(logger, thunk))
使用异步操作时的变化,ReduxTest.js
const mapDispatchToProps = { add: ()=>{ return { type: 'add' } }, minus: ()=>{ return { type: 'minus' } }, asyAdd: () => dispatch =>{ setTimeout(() => { // 异步结束后,手动调用 dispatch dispatch({ type: 'add' }) }, 1000); } }
代码抽取
抽离reducer和action
1、抽离action
action/reactReduxPage.js
export const add = ()=>{ return { type: 'add' } } export const minus= ()=>{ return { type: 'minus' } } export const asyAdd= () => dispatch =>{ setTimeout(() => { // 异步结束后,手动调用 dispatch dispatch({ type: 'add' }) }, 1000); }
// 对应的ReactReduxPage文件直接引用
2、抽离reducer
store/counterReducer.js
export default function counterReducer(state=0, action){ // console.log(state); switch (action.type){ case "add": return state + 1 case "minus": return state - 1 default: return state } }
reactReduxStore.js调用
import {createStore, applyMiddleware} from 'redux'; import logger from "redux-logger" import thunk from "redux-thunk"; import counterReducer from './counterReducer'; const store = createStore(counterReducer, applyMiddleware(logger, thunk)) export default store
如果有多个中间件 --- combineReducers
reactReduxStore.js
import {createStore, applyMiddleware, combineReducers} from 'redux'; import logger from "redux-logger" import thunk from "redux-thunk"; import counterReducer from './counterReducer'; import counterReducer2 from './counterReducer'; // 如果有多个中间件,则用combineReducers const store = createStore( combineReducers({ counter: counterReducer, counter2: counterReducer2 }), applyMiddleware(logger, thunk) ) export default store
ReactReduxPage.js做出修改
const mapStateToProps = state => { console.log(state); // {counter: 0, counter2: 0} // return需要做修改 // return { counter: state } return { counter: state.counter } }
Redux拓展
redux核心实现:
- 存储状态state
- 获取状态getState
- 更新状态dispatch
MyReduxPage.js
import React, {Component} from 'react' import store from '../../store/MyReduxStore'; // 这里做出了修改 export default class MyReduxPage extends Component { componentDidMount(){ store.subscribe(()=>{ this.setState({}) }) } render() { return ( <div> <h1>ReduxPage</h1> <p>counter:{store.getState()}</p> <button onClick={() => store.dispatch({type: 'add'})}>add</button> <button onClick={() => store.dispatch({type: 'minu'})}>minu</button> </div> ) } }
MyReduxStore.js
import {createStore} from '../kRedux'; function counterReducer(state=0, action){ // console.log(state); switch (action.type){ case "add": return state + 1 case "minu": return state - 1 default: return state } } const store = createStore(counterReducer) export default store
kRedux.js
export function createStore(reducer){ let currentState = undefined let currentListeners = [] function getState(){ return currentState } function dispatch(action){ currentState = reducer(currentState, action) currentListeners.map(cl=>cl()) } function subscribe(listener){ currentListeners.push(listener) } dispatch({type: '@IMOOC/REDUX'}) // 这里随意写type值,本意是要初始化执行一次 return { getState, dispatch, subscribe } }
中间件实现
kRedux.js
export function createStore(reducer, enhancer){ if(enhancer){ // 判断是否有增强器(中间件) return enhancer(createStore)(reducer) } let currentState = undefined let currentListeners = [] function getState(){ return currentState } function dispatch(action){ currentState = reducer(currentState, action) currentListeners.map(cl=>cl()) } function subscribe(listener){ currentListeners.push(listener) } dispatch({type: '@IMOOC/REDUX'}) // 这里随意写type值,本意是要初始化执行一次 return { getState, dispatch, subscribe } } // 中间件 export function applyMiddleware(...middlewares) { return createStore => (...arg) => { const store = createStore(...arg) const midApi = { getState: store.getState, dispatch: store.dispatch } const chain = middlewares.map(mw=>mw(midApi)) const dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } } function compose(...funcs){ const len = funcs.length if(len === 0) return arg=>arg if(len === 1) return funcs[0] return funcs.reduce((left, right)=>(...args)=>right(left(...args))) }
MyReduxStore.js
import {createStore, applyMiddleware} from '../kRedux'; function counterReducer(state=0, action){ // console.log(state); switch (action.type){ case "add": return state + 1 case "minu": return state - 1 default: return state } } const store = createStore(counterReducer, applyMiddleware(logger)) export default store function logger({dispatch, getState}){ return dispatch=>action=>{ console.log(action.type + "执行了~"); return dispatch(action) } }