【React】React全家桶(十)Redux

1 Redux概述

中文文档: http://www.redux.org.cn/

Github: https://github.com/reactjs/redux

1.1 Redux简介

Redux是一个专门用于做状态管理的JS库(不是react插件库)。它专门为React.js这门框架设计,但并不是只能用于react,可以用于任何界面库。 它的作用是集中式管理react应用中多个组件共享的状态。

安装:

npm i redux

使用场景

  • 某个组件的状态,需要让其他组件可以随时拿到(共享)
  • 一个组件需要改变另一个组件的状态(通信)

总体原则:能不用就不用,如果不用比较吃力才考虑使用

使用Redux的原因:

React仅仅是一个视图层的框架,解决的是数据与模板的渲染问题,但是并没有提供数据的状态管理方案,这在大型项目中是一个非常大的痛点 。

凡是复杂一些的应用都会有下图左边组件树的结构,非父子组件传递数据需要层层传递,如果项目稍微复杂,即使可以开发,但也变得不可维护。因此Redux应运而生。让Redux统一管理数据,任何组件对数据的操作,都交给redux管理,这样就大大提高了开发和维护的效率。

Redux用与不用的区别

在这里插入图片描述

1.2 Redux工作流程

在这里插入图片描述

首先React组件会在 Redux 中派发一个 action 方法,通过调用 store.dispatch 方法,将 action 对象派发给 store ,当 store 接收到 action 对象时,会将先前的 state 与传来的 action 一同发送给 reducerreducer 在接收到数据后,进行数据的更改,返回一个新的状态给 store ,最后由 store 更改 state

2 Redux三大核心概念

2.1 Action

action 是把数据从应用传到 store 的有效载荷,是 store 中唯一的数据来源,一般我们会通过调用 store.dispatch() 将 action 传到 store

我们需要传递的 action 是一个动作对象,它包含2个属性:

  • type:标识属性,值为字符串, 唯一, 必要属性
  • data:数据属性,,值类型任意, 可选属性

例如,这里我们暴露了一个用于返回一个 action 对象的方法,我们调用它时,会返回一个 action 对象

export const createIncrementAction = data => ({
    type: INCREMENT,
    data
})

2.2 Reducer

reducer 中,我们需要指定状态的操作类型,要做怎样的数据更新,reducer还会根据 action 的指示,对 state 进行对应的操作,然后返回操作后的 state 。总结:reducer用于初始化状态及加工状态( 根据 action 更新 state )

如下,我们对接收的 action 中传来的 type 进行判断,更改数据,返回新的状态

export default function countReducer(preState = initState, action) {
    const { type,data } = action;
    switch (type) {
        case INCREMENT:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            return preState
    }
}

2.3 Store

前面我们了解了action来描述“发生了什么”,以及使用 reducer来根据 action 更新 state 的用法。 而store就是将stateactionreducer联系在一起的对象

store 是 Redux 的核心,可以理解为是 Redux 的数据中台,我们可以将任何我们想要存放的数据放在store 中,在我们需要使用这些数据时,我们可以从中取出相应的数据。

Store 的作用如下

  • 维持应用的 state;

  • getState()(https://www.redux.org.cn/docs/api/Store.html#getState):获取 state

  • dispatch(action):分发action,触发reducer调用,产生新的state

  • subscribe(listener): 注册监听,当产生了新的state时, 自动调用

  • 具体编码:

    store.getState()
    store.dispatch({type:'INCREMENT', number})
    store.subscribe(render)
    

3 Redux API

3.1 createStore()

createStore():创建包含指定reducerstore对象

3.2 applyMiddleware()

applyMiddleware():应用上基于redux的中间件(插件库)

3.3 combineReducers()

combineReducers():合并多个reducer函数

4 使用redux编写应用

效果图

在这里插入图片描述

4.1 代码实现

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

// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore } from "redux";
// 引入为Count组件服务的reducer
import countReducer from "./count_reducer.js";
// 暴露store
export default createStore(countReducer);

redux/count_reducer.js:该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
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
  }
}

redux/count_action.js:该文件专门为Count组件生成action对象

import {INCREMENT,DECREMENT} from './constant'
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})

redux/constant.js:该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错

//将这两个单词写在 `constant` 文件中,并对外暴露,当我们需要使用时,我们可以引入这个文件,并直接使用它的名称即可,直接使用 `INCREMENT` 即可
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

Count/index.jsx

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

export default class Count extends Component {

  /* componentDidMount(){
    //检测redux中状态的变化,只要变化,就调用render
    store.subscribe(()=>{
      this.setState({})
    })
  } */

  //加法
  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(createIncrementAction(value*1))
    },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>
    )
  }
}

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'

ReactDOM.render(<App/>,document.getElementById('root'))

// 监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(()=>{
    ReactDOM.render(<App/>,document.getElementById('root'))
})

4.2 总结

store.js

  • 引入redux中的createStore函数,创建一个store
  • createStore调用时要传入一个为其服务的reducer
  • 记得暴露store对象

count_reducer.js

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

在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>

备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

5 Redux 三大原则

原则一单向数据流,整个 Redux 中,数据流向是单向的

UI 组件 —> action —> store —> reducer —> store

原则二state 只读,在 Redux 中不能通过直接改变 state ,来控制状态的改变,如果想要改变 state ,则需要触发一次 action。通过 action 执行 reducer

原则三纯函数执行,每一个reducer 都是一个纯函数,不会有任何副作用,返回是一个新的 state,state 改变会触发 store 中的 subscribe

6 redux-thunk异步编程

redux默认是不能进行异步处理的,某些时候应用中需要在redux中执行异步任务(ajax, 定时器),那么就需要使用异步中间件redux-thunk

6.1 redux-thunk的使用

redux-thunk的安装:

npm install redux-thunk

count_action.js:该模块专门为Count组件生成action对象

// 异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步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不是必须要用的
export const createIncrementAsyncAction = (data,time) => {
    return (dispatch)=>{
        setTimeout(()=>{
            dispatch(createIncrementAction(data))
        },time)
    }
}

store.js:该模块专门用于暴露一个store对象,整个应用只有一个store对象

//引入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))

6.2 总结

明确:延迟的动作不想交给组件自身,想交给action

何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。

具体编码

  1. npm install redux-thunk,并配置在store中
  2. 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
  3. 异步任务有结果后,分发一个同步的action去真正操作数据。

备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。

7 react-redux

react-redux:一个React插件库,专门用来简化React应用中使用redux

7.1 react-redux中组件分类

UI组件

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 通过props接收数据(一般数据和函数)
  • 不使用任何 Redux 的 API
  • 一般保存在components文件夹下

容器组件

  • 负责管理数据和业务逻辑,不负责UI的呈现
  • 使用 Redux 的 API
  • 一般保存在containers文件夹下

react-redux模型图

在这里插入图片描述

7.2 react-redux相关API

  • Provider:让所有组件都可以得到state数据

  • connect:用于包装 UI 组件生成容器组件

  • mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性

  • mapDispatchToProps:将分发action的函数转换为UI组件的标签属性

7.3 基本使用

安装react-redux

npm install react-redux

components/Count/index.jsx:UI组件

import React, { Component } from 'react'

export default class Count extends Component {
  //加法
  increment = ()=>{
    const {value} = this.selectNumber
    this.props.jia(value*1)
  }
  //减法
  decrement = ()=>{
    const {value} = this.selectNumber
    this.props.jian(value*1)
  }

  //奇数再加
  incrementIfOdd = ()=>{
    const {value} = this.selectNumber
    if(this.props.count % 2 !== 0){
      this.props.jia(value*1)
    }
  }
  //异步加
  incrementAsync = ()=>{
    const {value} = this.selectNumber
    this.props.jiaAsync(value*1,500)
  }

  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;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}

containers/Count/index.js:容器组件

//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction
} 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)),
    jian:number => dispatch(createDecrementAction(number)),
    jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
  }
}

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

App.jsx:给容器组件传递store

import React, { Component } from 'react'
import Count from './containers/Count'
import store from './redux/store'

export default class App extends Component {
  render() {
    return (
      <div>
        {/* 给容器组件传递store */}
        <Count store={store} />
      </div>
    )
  }
}

7.4 总结

明确两个概念

  • UI组件:不能使用任何 redux 的api,只负责页面的呈现、交互等。
  • 容器组件:负责和 redux 通信,将结果交给UI组件。

如何创建一个容器组件:靠 react-redux 的 connect 函数

  • connect(mapStateToProps,mapDispatchToProps)(UI组件)
  • mapStateToProps: 映射状态,返回值是一个对象
  • mapDispatchToProps: 映射操作状态的方法,返回值是一个对象

备注

  • 容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
  • mapDispatchToProps,也可以是一个对象

7.5 代码优化

优化1 :简写mapDispatchToProps

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

  //mapDispatchToProps的一般写法
  /* dispatch => ({
    jia:number => dispatch(createIncrementAction(number)),
    jian:number => dispatch(createDecrementAction(number)),
    jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
  }) */

  //mapDispatchToProps的简写
  {
    jia:createIncrementAction,
    jian:createDecrementAction,
    jiaAsync:createIncrementAsyncAction,
  }
)(CountUI)

优化2: Provider

容器组件可以检测redux中的状态改变,并渲染页面,所以不需要在index.js中检测了,不要在App.jsx中给子组件传递store了

// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'

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

优化3 :整合UI组件和容器组件

每个组件两个文件夹太麻烦了,直接整合在一起就好了

7.6 数据共享版

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

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

  state = {carName:'奔驰c63'}

  //加法
  increment = ()=>{
    const {value} = this.selectNumber
    this.props.jia(value*1)
  }
  //减法
  decrement = ()=>{
    const {value} = this.selectNumber
    this.props.jian(value*1)
  }
  //奇数再加
  incrementIfOdd = ()=>{
    const {value} = this.selectNumber
    if(this.props.count % 2 !== 0){
      this.props.jia(value*1)
    }
  }
  //异步加
  incrementAsync = ()=>{
    const {value} = this.selectNumber
    this.props.jiaAsync(value*1,500)
  }

  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;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}

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

  //mapDispatchToProps的一般写法
  /* dispatch => ({
    jia:number => dispatch(createIncrementAction(number)),
    jian:number => dispatch(createDecrementAction(number)),
    jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
  }) */

  //mapDispatchToProps的简写
  {
    jia:createIncrementAction,
    jian:createDecrementAction,
    jiaAsync:createIncrementAsyncAction,
  }
)(Count)

总结

  • 容器组件和UI组件整合一个文件

  • 无需自己给容器组件传递store,给包裹一个即可。

  • 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。

  • mapDispatchToProps也可以简单的写成一个对象

  • 一个组件要和redux“打交道”要经过哪几步?

    1. 定义好UI组件—不暴露

    2. 引入connect生成一个容器组件,并暴露,写法如下:

      connect(
      	state => ({key:value}), //映射状态
      	{key:xxxxxAction} //映射操作状态的方法
      )(UI组件)
      
    3. 在UI组件中通过this.props.xxxxxxx读取和操作转态

8 redux使用扩展

8.1 使用redux调试工具Redux Dev Tools

安装chrome浏览器插件Redux Dev Tools

8.2 下载工具依赖包

npm install redux-devtools-extension

在store中进行配置

import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

8.3 纯函数

reducer要求是一个纯函数,所以操作数组的时候,不能用push之类的方法

纯函数定义

  • 一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
  • 必须遵守以下一些约束
    • 不得改写参数数据
    • 不会产生任何副作用,例如网络请求,输入和输出设备
    • 不能调用Date.now()或者Math.random()等不纯的方法

9 最终版代码

注意

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

在这里插入图片描述

containers/Count/index.jsx

import React, { Component } from 'react'
//引入action
import {
  increment,
  decrement,
  incrementAsync
} from '../../redux/actions/count'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

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

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

  render() {
    //console.log('UI组件接收到的props是',this.props);
    return (
      <div>
        <h2>我是Count组件,下方组件总人数为:{this.props.renshu}</h2>
        <h4>当前求和为:{this.props.count}</h4>
        <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>
    )
  }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(
  state => ({
    count:state.count,
    personCount:state.persons.length
  }),
  {increment,decrement,incrementAsync}
)(Count)

containers/Person/index.jsx

import React, { Component } from 'react'
import {nanoid} from 'nanoid'
import {connect} from 'react-redux'
import {addPerson} from '../../redux/actions/person'

class Person extends Component {

  addPerson = ()=>{
    const name = this.nameNode.value
    const age = this.ageNode.value*1
    const personObj = {id:nanoid(),name,age}
    this.props.addPerson(personObj)
    this.nameNode.value = ''
    this.ageNode.value = ''
  }

  render() {
    return (
      <div>
        <h2>我是Person组件,上方组件求和为{this.props.count}</h2>
        <input ref={c=>this.nameNode = c} type="text" placeholder="输入名字"/>
        <input ref={c=>this.ageNode = c} type="text" placeholder="输入年龄"/>
        <button onClick={this.addPerson}>添加</button>
        <ul>
          {
            this.props.persons.map((p)=>{
              return <li key={p.id}>{p.name}--{p.age}</li>
            })
          }
        </ul>
      </div>
    )
  }
}

export default connect(
  state => ({
    persons:state.persons,
    count:state.count
  }),//映射状态
  {addPerson}//映射操作状态的方法
)(Person)

redux/actions/count.js

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

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

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const incrementAsync = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(increment(data));
    }, time);
  };
};

redux/action/person.js

import { ADD_PERSON } from "../constant";

//创建增加一个人的action动作对象
export const addPerson = (personObj) => ({ type: ADD_PERSON, data: personObj });

redux/reducers/count.js

/* 
  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('countReducer@#@#@#');
  //从action对象中获取:type、data
  const { type, data } = action;
  //根据type决定如何加工数据
  switch (type) {
    case INCREMENT: //如果是加
      return preState + data;
    case DECREMENT: //若果是减
      return preState - data;
    default:
      return preState;
  }
}

redux/reducers/person.js

import { ADD_PERSON } from "../constant";

//初始化人的列表
const initState = [{ id: "001", name: "tom", age: 18 }];

export default function personReducer(preState = initState, action) {
  const { type, data } = action;
  switch (type) {
    case ADD_PERSON: //若是添加一个人
      //preState.unshift(data) //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。
      return [data, ...preState];
    default:
      return preState;
  }
}

redux/reducers/index.js

/* 
    该文件用于汇总所有的reducer为一个总的reducer
*/
//引入combineReducers,用于汇总多个reducer
import { combineReducers } from "redux";
//引入为Count组件服务的reducer
import count from "./count";
//引入为Person组件服务的reducer
import persons from "./person";

//汇总所有的reducer变为一个总的reducer
export default combineReducers({
  count,
  persons,
});

redux/constant.js

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

redux/store.js

/* 
    该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import { createStore, applyMiddleware } from "redux";
//引入汇总之后的reducer
import reducer from "./reducers";
//引入redux-thunk,用于支持异步action
import thunk from "redux-thunk";
//引入redux-devtools-extension
import { composeWithDevTools } from "redux-devtools-extension";

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

App.jsx

import React, { Component } from 'react'
import Count from './containers/Count' //引入的Count的容器组件
import Person from './containers/Person' //引入的Person的容器组件

export default class App extends Component {
  render() {
    return (
      <div>
        <Count/>
        <hr/>
        <Person/>
      </div>
    )
  }
}

index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import store from "./redux/store";
import { Provider } from "react-redux";

ReactDOM.render(
  /* 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

展示

在这里插入图片描述

项目运行打包

npm run build
//全局安装serve
npm i serve -g
//运行
serve -s build
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值