5-react-数据处理

100 阅读13分钟

1. react请求

1. react发送ajax请求

发送请求需要在挂载完成或者数据更新之后

import React, {Component} from 'react'
import axios from 'axios'

class App extends Component {
  constructor () {
    super()
    this.state = {
      count: 0,
      isShow: true,
      msg: ''
    }
    console.log('constructor执行了');
  }
  render () {
    console.log('render执行了');
    return (
      <div>
        当前数据:{this.state.msg}
      </div>
    )
  }
  async componentDidMount () {
    const data = await axios({
      url: 'http://localhost:3000/content'
    }).then(data => data.data)
    console.log(data)
    this.setState(data[0])
    console.log('挂在完成')
  }
  shouldComponentUpdate () {
    console.log('更新');
    return true
  }
  componentDidUpdate () {
    console.log('更新已完成 ');
  }
}

export default App;

2. mock数据

在静态public文件中创建相关的文件夹书写数据

import React, {Component} from 'react'
import axios from 'axios'

class App extends Component {
  constructor () {
    super()
    this.state = {
      msg: []
    }
    console.log('constructor执行了');
  }
  async handler () {
    const data = await axios({
      url: '/mock/db.json'
    }).then(data => data.data)
    console.log(data);
    this.setState({
      msg: data
    })
  }
  render () {
    console.log('render执行了');
    return (
      <div>
        <button onClick={() => {this.handler()}}>点击</button>
        当前数据:{
            <ul>
            {
               this.state.msg.map(item => {
                   return (<li key={item.id}>{item.name}</li>)})
            }
            </ul>
        }
      </div>
    )
  }
}

export default App;

3. react请求转发

  • 请求转发
    由于存在跨域的问题,不同源 服务端对服务端不存在跨域问题
    所以如果请求服务a没有的数据跨域被服务器a转发给服务器b
  • 实现跨域请求的方法
    • 方法一:package.json代理
      在package.json中书写代理proxy属性,值为代理的端口,这样书写的请求路径只需要后面的路径,当请求发现没有前面的端口就会到package.json中寻找配置端口
      缺点是只能存储一个地址
    • 方法二:第三方包代理,http-proxy-middleware
      导入包中的createProxyMiddleware在setupProxy.js文件书写
// 方法一
// packkage.json
"proxy": "http://localhost:3005"
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// 路径对应代理的地址,然后开启同源跨域即可
// 第一个参数会和target进行拼接,请求的url需要比第一参数的路径要多或者相等
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);

2. redux

2.1 redux概念

  • 随着组建的增多,数据之间的数据交互会变的日益复杂,所以引入了redux进行数据的管理
  • store是数据存储的位置,reducers是数据操作的函数

2.2 创建store及reducer

  • store不属于任何组件,所以需要一个独立的文件进行存储
  • 安装redux,通过内部的createStore进行创建,接收的参数就是操作的数据
  • 通过createStore.getState()获取导入的数据
  • reduser是储存数据的文件
// 导入redux中的createStore方法

// index.js
import { createStore } from 'redux'
import handler1 from './store/reduser/a.reduser'
// 创建 传入一个方法
const store = createStore(handler1)
// 获取
console.log(store.getState())

// a.reduser
const handler1 = () => {
    return {
        count: 0
    }
}

export default handler1

2.3 获取store数据

  • 安装react-redux,在index.js中引入Provider并包裹App标签,传参类似于属性传参
  • 在需要引入数据的组件中引入react-redux中的connect方法,数据会存储在props中
  • conect方法中传入回调函数,并且会返回一个新函数,所以需要再次调用,接收的参数要获取数据的组件
  • connect中回调函数传入的参数是在Provider传入中的数据,回调函数中操作的对象就是props中的数据
  • 需要配合组件一起导出,不然props不能接收到数据

index.js

import { createStore } from 'redux'
import handler1 from './store/reduser/a.reduser'
import { Provider } from 'react-redux'


const store = createStore(handler1)
console.log(store.getState());

ReactDOM.render(
  <React.StrictMode>
    {/* 相当于是将Provider与后面的组件建立关系 */}
    <Provider store={store}><App /></Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

Counter

// 组件

import React, {Component} from 'react'
import {connect} from 'react-redux'

function Counter (props){
    return (
        <div>
            <button>-1</button>
            <span>{props.count}</span>
            <button>+1</button>
        </div>
    )
}

// 将index.js中导入的reducer数据传递到组件中
// Provider传递的参数就是reduser数据,也就是传入到store的参数
const storeProps = (state) => ({
    count: state.count
})
export default connect(storeProps)(Counter)

2.4 改变store数据

  • 通过传入组件中的dispatch()方法进行修改,在type配置中设置相关属性名
  • 在store文件中导出函数
    • 参数1是理解为数据占位符,保存相关的数据
    • 参数2是dispatch传入的配置

reduser数据 Counter.reducer.js

// Counter.reducer.js
const titleState = {
    count1: 6
}


const handler1 = (state = titleState, action) => {
    console.log(action);
    switch (action.type) {
        case 'addone':
            return {
                // 属性名要与数据中的titleState中的数据属性相同
                count1: ++state.count1
            }
        case 'removeone':
            return {
                count1: --state.count1
            }
        default:
            return state
    }
}

export default handler1

Counter

import React, {Component} from 'react'
import {connect} from 'react-redux'

class About extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }
    render () {
        return (
            <>
                <button onClick={() => {this.props.dispatch({type: 'removeone'})}}>-1</button>
                <span>{console.log(this.props)}</span>
                <span>{this.props.count}</span>
                <button onClick={() => {this.props.dispatch({type: 'addone'})}}>+1</button>
            </>
        )
    }
}

// store 参数是store.reduser中传出的数据,也就是state
const aboutStore = (store) => ({
    // 属性名就是传入props中使用的属性名
    count: store.count1
})

// connect意思是让组件和index.js中通过Provider导入的reduser数据进行联系
export default connect(aboutStore)(About)

2.5 action 传递参数

dispatch的配置会以对象的形式传入reducer函数的第二个参数,所以action.属性名即可调用配置的数据

Counter.reducer.js

// reducer
const titleState = {
    count1: 6
}


const handler1 = (state = titleState, action) => {
    console.log(action);
    switch (action.type) {
        case 'addone':
            return {
                // 属性名要与数据中的titleState中的数据属性相同
                count1: ++state.count1
            }
        case 'removeone':
            return {
                count1: --state.count1
            }
            case 'addn':
                return {
                    count1: state.count1 + action.payload
                }
        default:
            return state
    }
}

export default handler1

Counter

// 组件
import React, { Component } from 'react'
import { connect } from 'react-redux'

class Counter extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }
    render() {
        return (
            <>
                <button onClick={() => { props.dispatch({type: 'removeone'})}}>-1</button>
                <button onClick={() => { props.dispatch({type: 'addn', payload: 5})}}>+5</button>
                <span>{this.props.count}</span>
                <button onClick={() => { props.dispatch({type: 'addone'})}}>+1</button>
            </>
        )
    }
}

// store 参数是store.reduser中传出的数据,也就是state
const aboutStore = (store) => ({
    // 属性名就是传入props中使用的属性名
    count: store.count1
})

export default connect(aboutStore)(Counter)

2.6 提取action代码为函数

优化书写的reduser代码

  • 调用dispatch,并重新包裹dispatch
  • connect()两个回调函数
    第一个回调函数的参数是index.js中传入Provider的数据,也就是reduser中的数据,用于更新state,是输入源
    第二个回调函数的参数,用于action,输出源,也就是执行的方法
// 组件
import React, { Component } from 'react'
import { connect } from 'react-redux'

class About extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }
    render() {
        return (
            <>
                <button onClick={() => { this.props.removeone()}}>-1</button>
                <button onClick={() => { this.props.addn(5)}}>+5</button>
                <span>{console.log(this.props)}</span>
                <span>{this.props.count}</span>
                <button onClick={() => { this.props.addone() }}>+1</button>
            </>
        )
    }
}
// store 参数是store.reduser中传出的数据,也就是state
const aboutStore = (store) => ({
    // 属性名就是传入props中使用的属性名
    count: store.count1
})
const mapState = dispatch => ({
    addone(){
        dispatch({ type: 'addone' })
    },
    removeone () {
        dispatch({type: 'removeone'})
    },
    addn (n) {
        dispatch({type: 'addn', payload: n})
    }
})

export default connect(aboutStore, mapState)(About)

2.7 自动生成action触发函数

  • 需要借助redux中的bindActionCreators方法
    第一个参数是对象,存储的方法名称
    第二个参数是dispatch
  • bindActionCreators会返回一个新对象,但是connect的第二个参数返回的是一个对象,所以需要解构bindActionCreators再放入connect第二个参数
  • 第一个参数对象内保存的是函数形式的方法
// 基本用法
bindActionCreators({
    addone() {
        return { type: 'addone'}
    }
}, dispatch )
// 组件
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as j from '../store/actions/action'

console.log(j);

class About extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }
    render() {
        return (
            <>
                <button onClick={() => { this.props.removeone()}}>-1</button>
                <button onClick={() => { this.props.addn(5)}}>+5</button>
                <span>{console.log(this.props)}</span>
                <span>{this.props.count}</span>
                <button onClick={() => { this.props.addone() }}>+1</button>
            </>
        )
    }
}

// store 参数是store.reduser中传出的数据,也就是state
const aboutStore = (store) => ({
    // 属性名就是传入props中使用的属性名
    count: store.count
})

const mapState = dispatch => ({
    ...bindActionCreators(j, dispatch)
})

export default connect(aboutStore, mapState)(About)

actions.js

// actions
export const addone = () => ({type: 'addone'})
export const removeone = () =>  ({type: 'removeone'})
export const addn = (payload) =>  ({type: 'addn', payload: payload})

2.8 设置action类型常量

多次引用,方便使用,优化复制

声明
export const ADDONE = 'addone'
export const ADDN = 'addn'
export const REMOVEONE = 'removeone'

使用
import { ADDONE, REMOVEONE } form '.action_type'

2.9 redux拆分与合并

  • 在多组件中,将多种将变量,方法,数据分别存放在不同的文件中,通过导入的操作导入到相关的文件,然后再传入组件中
  • redux中导入combineReducers()方法,参数是配置对象
  • 存放数据的文件中保存着数据和reducer相关的方法
  • 组件中存放相关的方法,并用bindActionCreators进行存储

1. 数据存储操作的js

/reducer/person.reducer.js

// 数据存储操作的js
import { ADDPERSON } from '../action_type/person.type'

const personStore = [
    {
        id: 1,
        name: 'zs'
    }
]

const person = (state = personStore, action) => {
    switch (action.type) {
        case ADDPERSON:
            return [
                ...state,
                action.payload
            ]
        default:
            return state
    }
}

export default person

2. reducer集合

/reducer/index.js

import {combineReducers} form 'redux'
import personReducer form './Person.reducer'
import counterReducer form './Counter.reducer'
export default combineReducers({
    counter: counterReducer,
    person: personReducer
})

3. 根目录index.js

index.js

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 引入创建store数据的方法
import {createStore} from 'redux'
// 引入数据(上一个文件)
import totalStore from './store/reducers/index.js'
// 数据建立联系
import {Provider} from 'react-redux'
const store = createStore(totalStore)

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

4. 组件

/components/Person.js

// 生成action触发函数的方法,参数1是对象,参数2是相关的函数
import React from 'react';
import { bindActionCreators } from 'redux'
// 用于让store和组件产生联系
import { connect } from 'react-redux'
import * as personActions from '../store/actions/person.actions'

function Person(props) {
    return (
        <div>
            <button onClick={() => { props.addPerson({ id: props.personReducer.length + 1, name: 'lis' }) }}>点击添加</button>
            <ul>
                {props.personReducer.map(item => {
                    return (
                        <li key={item.id}>{item.name}</li>
                    )
                })}
            </ul>
        </div>
    )
}

const personStore = state => ({
    personReducer: state.person
})

const personAction = dispatch => ({
    ...bindActionCreators(personActions, dispatch)
})

export default connect(personStore, personAction)(Person)

5. 存放方法的js

/action/person.action.js

// 存放方法的js
import { ADDPERSON } from './action.type'

export const addPerson = (payload) => ({ type: ADDPERSON, payload })

6. 存放变量类型的js

/action_type/person.action.typse.js

// 存放变量类型的js
export const ADDPERSON = 'addPerson'

2.7 redux 工作流程梳理

第一大步

  1. 创建数据store并保存,关联reducer
  2. 在index.js中引入redux的createStore方法,引入需要store管理的数据
  3. 通过createStore存储数据
import { createStore } form 'redux'
const store = createStore()
  1. 创建reducer,参数含义
    1. state:store接收到相关指令之后,就会将state传递给相应的reducer,reducer通过处理会返回一个结果,简单的说,state就是返回给store进行存储的数据
    2. action:store传递给reducer的具体指令,action.payload存储传入的数据
  2. reducer通过判断action.type进行数据的分支处理
function reducer (state = initState, action) {
    switch(action.type){
        case 'add':
            return {
                content : [
                    ...state.content
                    action.content
                ]
            }
    }
}
  1. 给state定义初始数据
const initState = {
   content: ['默认数据']
}

第二大步

  1. 第二大步,store向组件进行传递
  2. 导入react-redux中的Provider标签,并通过store属性传递store
import {Provider} from 'react-redux'
const store = createStore(totalStore)

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}><App /></Provider>
  </React.StrictMode>,
  document.getElementById('root')
);
  1. 需要用到store数据的子组件中,通过react-redux中的connect进行关联
    1. connect调用后返回的是一个函数,返回函数的参数是想要关联的组件
import { connect } from 'react-redux'

export default connect()(App)
  1. 在具体的组件中使用connect方法获取store里保存的数据,通过组件的props进行访问
  2. connect内部参数是两个函数方法,最终都可以通过props进行接收
    1. 参数1,是store数据,目的是操作数据,获取组件需要的数据
    2. 参数2,是dispatch重新包裹的方法
      1. dispatch => (函数名()=> dispatch({配置项})),最原始的方法
      2. dispatch可以通过bindActionCreators进行函数触发事件的绑定,优化分离的方法
      3. bindActionCreator
        • 第一个参数是函数方法的配置型,也就是多个原始方法
        • 第二个参数是dispatch
  3. 获取数据之后可以在界面上渲染
import { connect } from 'react-redux'

class App extends Component {
    render() {
        return (
            <>
                <input type="text" ref={this.myRef}
                <button onClick={this.handler}>add</button>
                <ul>
                {
                    this.props.conent.map((item, index) => <li key={index}>内容{item}</li>)
                }
                </ul>
            </>
        )
    }
}

// 从store当众获取当前组件需要使用的数据
const mapStateToProps = (state) => ({
    content: state.content
})

export default connect(mapStateToProps)(App)
  1. 数据更改后自动更新界面
    1. 输入框中的内容需要先获取元素
    2. 通过React.createRef()创建对象
    3. 让输入框的ref属性绑定给React.createRef()就可以获取
    4. 通过React.createRef().current获取绑定了React.createRef()元素的DOM节点
  constructor () {
    super()
    this.myRef = React.createRef()
  }
  handler = () => {
    const content = this.myRef.current.value
    this.props.dispatch({type: 'add', content})
    this.myRef.current.value = ''
  }

简单版

  1. 创建store保存相关数据关联reducer
  2. 利用provider将store向后传递
  3. 在具体的组件当中使用connect方法获取store里面保存的数据
  4. 当我们拿到数据之后就可以在界面上渲染了

2.8 redux使用代码优化

--src
   |--store
   |    |--Reduces
   |    |    |--Content.reducer.js
   |    |    |--index.js
   |    |--Actions
   |    |    |--Content.action.js
   |    |--Action_types
   |         |--Content.action.types.js
   |
   |--App.js
   |--index.js

Content.reducer.js

const initState = {
   content: ['默认数据']
}

function reducer (state = initState, action) {
    switch(action.type){
        case 'add':
            return {
                content : [
                    ...state.content
                    action.content
                ]
            }
    }
}

export default reducer

/reducer/index.js

import {combineReducers} form 'redux'
import contentReducer form './Content.reducer'
export default combineReducers({
    contentReducer: contentReducer
})

index.js

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// 引入创建store数据的方法
import {createStore} from 'redux'
// 引入数据(上一个文件)
import totalStore from './store/reducers/index.js'
// 数据建立联系
import {Provider} from 'react-redux'
const store = createStore(totalStore)

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

/action/Content.action.js

// 存放方法的js
import { ADD } from './action.type'

export const add = (content) => ({ type: ADD, content })

App.js

import { connect } from 'react-redux'

class App extends Component {
    render() {
        return (
            <>
                <input type="text" ref={this.myRef}
                <button onClick={this.handler}>add</button>
                <ul>
                {
                    this.props.conent.map((item, index) => <li key={index}>内容{item}</li>)
                }
                </ul>
            </>
        )
    }
}

// 从store当众获取当前组件需要使用的数据
const mapStateToProps = (state) => ({
    content: state.content
})
const mapDispatchToProps = (dispatch) => {
    ...bindActionCreators(contentActions, dispatch)
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

2.9 redux中间件工作流程

  • 中间件的引入是为了让异步执行的操作先做完
  • 中间件的本质也是函数,他会在组件发送指令时触发,也就是action操作时触发
  • 需要从redux中导入applyMiddleware方法,并作为createStore的第二个参数(第一个参数是reducer),内部存储的是函数方法,内部的函数方法需要返回一个函数内部,该函数内部书写异步任务,异步的操作依旧是需要返回一个函数,此函数就能触发生效
  • store.dispatch({配置体})是直接执行,包裹的函数调用了才会触发
  • 在异步执行完成后,还需要将action给store继续往后传递
// index.js
import {createStore, applyMiddleware} from 'redux'

function reducer(state, action) {
  return state
}

// middle 参数1,是store的数据
// 参数2是操作action操作时,操作转交,目的是为了提前进行action的相关操作,再结合后面的action参数
function middle(getState, dispatch) {
  // 参数是向后传递的方法,要传递到redux的数据通过next方法包裹
  return function (next) {
    return function (action) {
      // 参数保存的是action的配置
      // 需要操作数据向后面传递时,那么就可以通过属性交给action
      action.payload = 100
      next(action)
    }
  }
}

const store = createStore(reducer, applyMiddleware(middle))
store.dispatch({type: 'test'})

2.10 redux异步解决方案

2.10.1 redux-thunk异步解决方案

  • 安装redux-thunk,在applyMiddleware()中进行引入
  • 在原本存放dispatch方法的文件中就可以返回一个函数,函数的参数是dispatch,可以直接触发后续的操作
// person.action.js
import axios from 'axios'

export const addPerson = () => async (dispatch) => {
    let person = await axios.get("地址").then(res => res.data)
    // 直接传递给reducer中的action
    dispatch({type: 'loadPersonSuccess', payload: person})
}
// index.js
import {createStore, applyMiddleware } form 'redux'
improt thunk from 'redux-thunk'

const store = createStore(reducer, applyMiddleware(thunk))

2.10.2 react-saga异步数据解决方案

  • 安装redux-saga,引入默认方法createSagaMiddleware,在applyMiddleware中注册createSageMiddleware(),然后给createSageMiddleware().run
  • createSageMiddleware().run方法内部参数是sage文件输出的方法需要在applyMiddleware()注册之后
  • saga文件中引入takeEvery和put方法
  • takeEvery负责拦截进行的操作,接收指令,参数1是拦截的dispatch指令名,参数2是执行方法
  • put负责重新触发dispatch后续指令,传递给store,也就是reducer中的action
// sage.js
import axios from 'axios'
import {takeEvery, put} from 'redux-saga/effects'

function* addPerson() {
    let persons = yield axios.get("地址").then(res => res.data)
    // 重新触发dispatch
    yield put({type: 'addPerson_success', payload: persons})
}

export default function* personSage() {
    // 拦截的参数
    yield takeEvery('addPerson', addPerson)
}
// app.js
import React, {Component} from 'react'
import Person from './components/Person'
import {connect} from 'react-redux'
// 生成action触发函数的方法,参数1是对象,参数2是相关的函数
import { bindActionCreators } from 'redux'

import * as personActions from './store/actions/person.actions'

class App extends Component {
  render () {
    return (
      <div>
        {console.log(this.props)}
        <button onClick={() => {this.handler()}}>获取数据</button>
          <Person />
      </div>
    )
  }
  handler () {
    console.log(123)
    this.props.addPerson()
  }
}

const mapStateProps = (state) => ({
  perosn: state.personReducer
})

const mapActionProps = dispatch => {
  return {
    ...bindActionCreators(personActions, dispatch)
  }
}

export default connect(mapStateProps, mapActionProps)(App);
// index.js
import createSagaMiddleware from 'redux-saga'

import personSage from './store/saga/person.saga'
// 注册为中间件
const sagaMiddleware = createSagaMiddleware()
console.log(sagaMiddleware);

const store = createStore(totalStore, applyMiddleware(sagaMiddleware))
// 让saga的方法与组件产生联系
sagaMiddleware.run(personSage)

store.dispatch({type: 'test'})

2.10.3 redux-saga拆分与合并

  • 在存放方法的root.saga文件中中导入redux-saga/effects中的all方法,导出函数
  • all方法参数接收的是数组,内部存放的是多有的saga方法
  • 在index.js中,用redux-saga默认导出的方法.run()方法进行注册
  • 内部需要的是执行的结果,使用数组内的函数需要调用
// root.saga.js
import {all} from 'redux-saga/effects'
import personSaga from './person.saga'

export default function* rootSaga() {
    yield all([
        personSaga()
    ])
}

// index.js
...
import createSagaMiddleware from 'redux-saga'

import rootSage from './store/saga/root.sage'
// 注册为中间件
const sagaMiddleware = createSagaMiddleware()
console.log(sagaMiddleware);

const store = createStore(totalStore, applyMiddleware(sagaMiddleware))
sagaMiddleware.run(rootSage)
...

2.11 简化reducer和action

  • 安装redux-actions
  • 在actions中引入creatAction方法,内部的参数是指令的名称,并且导出返回一个新的函数值
  • 这reducer文件中引入handlerActions,并导出整体的配置
    • 参数1,配置的对象的reducer方法
    • 参数2,初始值
// actions.js
import {createAction} from 'redux-actions'
import {INCREMENT, DECREMENT} from '../action_type/counter.type'

// export const increment = () => ({type: INCREMENT})
// export const decrement = () => ({type: DECREMENT})


export const decrement_actions = createAction(DECREMENT)
export const increment_actions = createAction(INCREMENT)
// reducer.js
import {handleActions as createReducer} from 'redux-actions'
import {decrement_actions, increment_actions} from '../actions/counter.actions'

const titleState = {
    count: 0
}

// 参数1是分开的指令,参数2是当前的初始值
export default createReducer({
[decrement_actions]: (state, action) => ({count: state.count - 1}),
[increment_actions]: (state, action) => ({count: state.count + 1})
}, titleState)

// export default function AppState(state = titleState, action) {
//     switch (action.type) {
//         case INCREMENT:
//             return {
//                 count: state.count + 1
//             }
//         case DECREMENT:
//             return {
//                 count: state.count - 1
//             }
//         default:
//             return state
//     }
// }
// app.js
import {connect} from 'react-redux'
import { bindActionCreators } from 'redux';
import * as counterActions from './store/actions/counter.actions'


function App(props) {
  return (
    <div>
      <button onClick={() => {props.decrement_actions()}}>-1</button>
      <span>{props.count}</span>
      <button onClick={() => {props.increment_actions()}}>+1</button>
    </div>
  );
}

const mapAppProps = state => ({
  count: state.counter.count
})
const mapAppActions = dispatch => ({
  ...bindActionCreators(counterActions, dispatch)
})

export default connect(mapAppProps, mapAppActions)(App)