三、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上手

Redux是JavaScript应用的状态容器。它保证程序行为一致性且易于测试。
 
安装redux: npm install redux --save
用一个累加器举例:
1、需要一个store来存储数据
2、store里的reducer初始化state并定义state修改规则
3、通过dispatch一个action来提交对数据的修改
4、action提交到reducer函数里,根据传入的action的type,返回新的state
 
创建store ,src/store/ReduxStore.js
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文件直接引用

import React, { Component } from 'react'
import {connect} from 'react-redux';
import {add, minus,asyAdd} from '../action/reactReduxPage'; // 此处直接引用

class ReactReduxPage extends Component {
  render() {
    console.log(this.props);
    const {counter, add, minus, asyAdd} = this.props
    return (
      <div>
        <h1>ReactReduxPage</h1>
        <p>counter:{counter}</p>
        <button onClick={add}>add</button>
        <button onClick={minus}>minus</button>
        <button onClick={asyAdd}>asyAdd</button>
      </div>
    )
  }
}
const mapStateToProps = state => {
  return {
    counter: state
  }
}
const mapDispatchToProps = {
  add, 
  minus,
  asyAdd
}
export default connect(
  // mapStateToProps
  mapStateToProps,
  // mapDispatchToProps --- 直接引用
  mapDispatchToProps
)(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)
  }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-10-18 17:31  落叶无痕~  阅读(450)  评论(0编辑  收藏  举报