开发中配置&优化

目录

  1. .src下建立:
  2. -redux
  3. -store.js
  4. -reducers.js
  5. -axxReducer.js
  6. -bxxReducer.js
  7. -actionTypes.js -放置容易写错的type
  8. -actionCreators.js -专门用于创建action对象

优化

全过程基础优化配置

/****actionTypes.js 定义type值****/
//添加事件
export const TO_ADD = 'toAdd'
//删除事件
export const TO_DEL = 'toDel'


/****actionCreators.js 创建action对象****/
import { TO_ADD , TO_DEL } from 'actionTypes.js'
import store from ''
export const addFnCreatore = () =>{
  let action = {
    type:TO_ADD
  }
  store.dispatch(action)
}


/****axxReducer.js 定义触发修改state****/
import { TO_ADD , TO_DEL } from 'actionTypes.js'
switch (){
  case TO_ADD:
  //...
}


/****组件调用****/
import {addFnCreatore} from 'actionCreators.js'
addFnCreatore()

如果项目复杂可继续优化

/****actionCreators.js 一个函数即可, 其他通过传参****/
import store from ''
export defaul (type,value) =>{
  let action = {
    type,
    value
  }
  store.dispatch(action)
}


/****组件调用****/
import { TO_ADD , TO_DEL } from 'actionTypes.js'
import creator from 'actionCreators.js'

creator(TO_ADD)

reducer复杂时 可以将case内容抽离出来

image.png

redux

image.png

原理图&简易代码

redux原理图.png

 function createStore(reducer){
   var list = []
   var state = reducer(undefined,{})

   function subscribe(callback){
      list.push(callback)
   }

   function dispatch(action){
      state = reducer(state,action)
      for(var i in  list){
         list[i] && list[i]()
      }
   }

   function getState(){
      return state
   }
   return {
      subscribe,
      dispatch,
      getState
   }
 }

理解

安装: npm install redux redux-thunk

    (1).去除Count组件自身的状态
    (2).src下建立:
                    -redux
                        -store.js
                        -count_reducer.js
            -count_action.js 专门用于创建action对象
            -constant.js 放置容易写错的type值

    (3).store.js:
                1).引入redux中的createStore函数,创建一个store
                2).createStore调用时要传入一个为其服务的reducer
                3).记得暴露store对象

    (4).count_reducer.js:
                1).reducer的本质是一个函数,接收:preState,action,返回加工后的状态
                2).reducer有两个作用:初始化状态,加工状态
                3).reducer被第一次调用时,是store自动触发的,
                                传递的preState是undefined,
                                传递的action是:{type:'@@REDUX/INIT_a.2.b.4}

    (5).在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>
            备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

store.js - 暴露store对象

/* 
    该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/

//1. 引入createStore,专门用于创建redux中最为核心的store对象
//applyMiddleware用于引入中间件  //combineReducers用于合并多个thunk
import {createStore,applyMiddleware,combineReducers} from 'redux'

//2. 引入为City组件和Tabbar服务的reducer (每个功能单独作为一个reducer)
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
import countReducer2 from './count_reducer2'
//合并reducer
 const reducer  = combineReducers({
  countReducer,
  countReducer2
 })

//3. 引入中间件 下列两种二选一或全选都可以
//用于支持异步action 回调函数写法
import reduxThunk from 'redux-thunk'
//用于支持异步action promise写法
import reduxPromise from 'redux-promise'

//4. 暴露store
export default createStore(reducer,applyMiddleware(reduxThunk,reduxPromise))

使用redux-tools时

//5.如果使用redux-tools需要改写为
 import {applyMiddleware, combineReducers, createStore,compose} from 'redux'
 import countReducer from './count_reducer'
 import countReducer2 from './count_reducer2'
 import reduxThunk from 'redux-thunk'
 import reduxPromise from 'redux-promise'
 const reducer  = combineReducers({
  countReducer,
  countReducer2
 })

 const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

 const store = createStore(reducer,composeEnhancers(applyMiddleware(reduxThunk,reduxPromise)));

constant.js - 定义type常量值

/* 
    该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

count_reducer.js - switch函数

/* 
    1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
    2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from './constant'

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
    // console.log(preState);
    //从action对象中获取:type、data
    const {type,data} = action
    //根据type决定如何加工数据
    switch (type) {
        case INCREMENT: //如果是加
            return preState + data
        case DECREMENT: //若果是减
            return preState - data
        default:
            return preState
    }
}

count_action.js - {type,data}

/* 
    该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from './constant'

//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
//这是redux-thunk写法(函数写法) 也可以使用redux-promise写法(promise写法)
export const createIncrementAsyncAction = (data,time) => {
    return (dispatch)=>{
        setTimeout(()=>{
            dispatch(createIncrementAction(data))
        },time)
    }
}

xxx组件 - subscribe监测

store.subscribe()监测store中状态的改变,一旦发生改变则获取数据再进行其他逻辑处理

import React,{useState,useEffect} from 'react'
import store from '../redux/store'
export default function Cinemas(props) {
    useEffect(() => {
        //订阅
        var unsubscribe = store.subscribe(()=>{
            console.log("cinema 中订阅",store.getState().countReducer)
          //获取数据后可以将数据set为自身数据
        })
        return ()=>{
            //取消订阅?
            unsubscribe() 
        }
    }, [])
}

Count组件 - dispatch触发

import React, { Component } from 'react'
//引入store,用于获取redux中保存状态
import store from '../../redux/store'
//引入actionCreator,专门用于创建action对象
import {
    createIncrementAction,
    createDecrementAction,
    createIncrementAsyncAction
} from '../../redux/count_action'

export default class Count extends Component {

    //加法
    increment = ()=>{
        const {value} = this.selectNumber
        store.dispatch(createIncrementAction(value*1))
    }
    //减法
    decrement = ()=>{
        const {value} = this.selectNumber
        store.dispatch(createDecrementAction(value*1))
    }
    //奇数再加
    incrementIfOdd = ()=>{
        const {value} = this.selectNumber
        const count = store.getState()
        if(count % 2 !== 0){
            store.dispatch(createIncrementAction(value*1))
        }
    }
    //异步加
    incrementAsync = ()=>{
        const {value} = this.selectNumber
        // setTimeout(()=>{
            store.dispatch(createIncrementAsyncAction(value*1,500))
        // },500)
    }

    render() {
        return (
            <div>
                <h1>当前求和为:{store.getState()}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;
                <button onClick={this.increment}>+</button>&nbsp;
                <button onClick={this.decrement}>-</button>&nbsp;
                <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
                <button onClick={this.incrementAsync}>异步加</button>&nbsp;
            </div>
        )
    }
}

redux异步action版

     (1).明确:延迟的动作不想交给组件自身,想交给action
     (2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
     (3).具体编码:
                 1).yarn add redux-thunk,并配置在store中
                 2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
                 3).异步任务有结果后,分发一个同步的action去真正操作数据。
     (4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer,applyMiddleware(thunk))

react-redux

image.png
在redux建立的文件情况下修改

原理图

react-redux模型图.png

整体结构

src
    -containers    -放置UI加容器组件 
      -Count
        -index.jsx    -cout容器组件和UI组件
    -Person    
        -index.jsx    -person容器组件和UI组件

  -redux    -放置redux相关文件
      -actions    -每个组件的action文件
        -count.js
      -person.js
    -reducers    -每个组件的reducers文件
        -index.js -汇总reducers给store
      -count.js
      -person.js

    -constant.js    -放置容易写错的type值
    -store.js

理解

npm install redux redux-thunk
npm install react-redux

        (1).明确两个概念:
                    1).UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
                    2).容器组件:负责和redux通信,将结果交给UI组件。
        (2).如何创建一个容器组件————靠react-redux 的 connect函数
                        connect(mapStateToProps,mapDispatchToProps)(UI组件)
                            -mapStateToProps:映射状态,返回值是一个对象
                            -mapDispatchToProps:映射操作状态的方法,返回值是一个对象
        (3).备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
        (4).备注2:mapDispatchToProps,也可以是一个对象

App.js

给count容器组件传递store
import store from './redux/store'
render() {
return (
<div><Count store={store} /> </div>
)
}

容器组件代码

//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import {
    createIncrementAction
} from '../../redux/count_action'

//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

/* 
    1.mapStateToProps函数返回的是一个对象;
    2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
    3.mapStateToProps用于传递状态
*/
function mapStateToProps(state){
    return {count:state}
}

/* 
    1.mapDispatchToProps函数返回的是一个对象;
    2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
    3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch){
    return {
        jia:number => dispatch(createIncrementAction(number))
    }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

count UI 组件调用 this.props.jia(value*1)

模拟connenct方法源码

//UI组件
import React,{useEffect} from 'react'
function NotFound(props) {
    useEffect(() => {
        console.log(props)
    }, [props])
    return (
        <div>
            404 not found
        </div>
    )
}
//模拟connenct方法(高阶函数)
function kerwinconnenct(cb,obj){
    //接收state
    var value = cb()
    //接收组件
    return (MyComponent)=>{
        // props 路由方法...
        return (props)=>{

            return <div style={{color:"red"}}>
                //obj(dispatch方法)
                <MyComponent {...value} {...props} {...obj}/>
            </div>
        }
    }
}
//调用传参(高阶组件)
export default kerwinconnenct(()=>{
    return {
        a:1,
        b:2
    }
},{
    aa(){},
    bb(){}
})(NotFound)

react-redux优化

        (1).容器组件和UI组件整合一个文件
        (2).无需自己给容器组件传递store,给<App/>包裹一个<Provider store={store}>即可。
        (3).使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
        (4).mapDispatchToProps也可以简单的写成一个对象
        (5).一个组件要和redux“打交道”要经过哪几步?
                        (1).定义好UI组件---不暴露
                        (2).引入connect生成一个容器组件,并暴露,写法如下:
                                connect(
                                    state => ({key:value}), //映射状态
                                    {key:xxxxxAction} //映射操作状态的方法
                                )(UI组件)
                        (4).在UI组件中通过this.props.xxxxxxx读取和操作状态

容器组件融合UI组件代码

import React, { Component } from 'react'
//引入action
import {
    createIncrementAction
} from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

//定义UI组件
class Count extends Component {

    //加法
    increment = ()=>{
        const {value} = this.selectNumber
        this.props.jia(value*1)
    }

    render() {
        //console.log('UI组件接收到的props是',this.props);
        return (
            <div>
                <h1>当前求和为:{this.props.count}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>&nbsp;
                <button onClick={this.increment}>+</button>&nbsp;
            </div>
        )
    }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(
    state => ({count:state}),
    //mapDispatchToProps的简写
    {
        jia:createIncrementAction
    }
)(Count)

App.js

由Provider统一传递store
render() {
return (
<div><Count ~~store={store}~~ /> </div>
)
}

index.js

import store from './redux/store'
import {Provider} from 'react-redux'

ReactDOM.render(
    <Provider store={store}>
        <App/>
    </Provider>,
    document.getElementById('root')
)

redux持久化 (localStorage)

安装: npm install redux-persist

//store.js
...
import {persistStore, persistReducer} from 'redux-persist';
import storage from 'redux-persist/lib/storage';
//未知import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
const persistConfig = {
  key: 'kerwin',
  storage: storage,
  whitelist: ['CityReducer'], //指定白名单(需要持久化的reduce) 不指定则默认所有
  //localStorage: import storage from 'redux-persist/lib/storage'
  //sessionStorage: import storageSession from 'redux-persist/lib/storage/session'
  //stateReconciler: autoMergeLevel2 //控制在本地存储中,新老状态怎么合并,覆盖?或者合并?

};
//改造reducer
const persistedReducer = persistReducer(persistConfig, reducer)
//改造store
const store = createStore(persistedReducer,...);
const persistor = persistStore(store)
//导出
export {store,persistor}



//改造根组件 index.js
import {persistor} from './Store'
import {PersistGate} from 'redux-persist/lib/integration/react';
<PersistGate loading={null} persistor={persistor}>
...
</PersistGate>

求和案例_react-redux数据共享版

        (1).定义一个Pserson组件,和Count组件通过redux共享数据。
        (2).为Person组件编写:reducer、action,配置constant常量。
        (3).重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,
                合并后的总状态是一个对象!!!
        (4).交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。

Person 的redux相关文件

image.png

Person和Count 组件数据共享

image.png

求和案例_react-redux最终版

        (1).所有变量名字要规范,尽量触发对象的简写形式。
        (2).reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer

reducers/index.js

import {combineReducers} from 'redux'
//引入为Count组件服务的reducer
import count from './count';
import persons from'./person';

export default combineReducers({
    count,
    persons
});

store.js 引入reducers

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import reducer from './reducers'

//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk';

//暴露store
export default createStore(reducer,applyMiddleware(thunk))

求和案例_react-redux开发者工具的使用

        (1).npm install redux-devtools-extension
        (2).store中进行配置
                import {composeWithDevTools} from 'redux-devtools-extension'
                const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

store.js

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import reducer from './reducers'

//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk';

import {composeWithDevTools} from 'redux-devtools-extension'

//暴露store
export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))

redux持久化

npm install redux-persist
集体看GitHub 以下是基本配置

store.js 这里将CollApsedReducer持久化

import {legacy_createStore as createStore,combineReducers} from 'redux'
import {CollApsedReducer} from './reducers/CollapsedReducer'
import {LoadingReducer} from './reducers/LoadingReducer'
//1.持久化redux
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
//2.存入storage的规则
const persistConfig = {
  key: 'loading',
  storage,
  blacklist: ['LoadingReducer'] //黑名单
}

const reducer = combineReducers({
  CollApsedReducer,
  LoadingReducer
})
//3. 
const persistedReducer = persistReducer(persistConfig, reducer)
//4.
const store = createStore(persistedReducer,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
//5.
const persistor = persistStore(store)
//6.
export {
  store,
  persistor
}

app.js


import {Provider } from 'react-redux'
import IndexRouter  from './router/IndexRouter'
import { store,persistor } from './redux/store'
import { PersistGate } from 'redux-persist/integration/react'

function App(){
  return  <Provider store = {store}>
    //***
    <PersistGate loading={null} persistor={persistor}>
      <IndexRouter></IndexRouter>
    </PersistGate>
  </Provider>
}

export default App