React-redux从0到1的过程

968 阅读11分钟

1.redux工作原理

1615515774076-4b37db83-6c13-48cc-a030-dc0094a1f7e2[1].png

  • 1.React components是我们自己定义的组件
  • 2.React components会把需要做的事告诉Action Creators需要做什么
  • 3.Action Creators会创建一个动作对象action
  • 4.action中包含两个属性:type:动作类型,data:操作的数据
  • 5.Action Creators将action派发(dispatch)进入Store中,dispatch可以将动作action对象传递下去,所以必须要有
  • 6.进入Store,Store是核心仓库,负责全局掌控,本身不处理事件,就是一个指挥者
  • 7.Store将需要处理的action对象给Reducers,同时将之前状态的动作对象也传递给Reducers
  • 8.Reducer结合之前的action与当前的action进行事件处理,并将新的状态结果返回给Store,上图的return newState
  • 9.Store将新的状态结果通过getState()传递给React components,改变组件当前状态 Reducers可以加工状态也可以初始化状态(只做一次,就是第一次),当Reducer初始化状态时,有action对象,previousState为undefined,传递的action中的type就是初始化,data是初始化的数据

2.redux初步实践

2.1 安装redux

npm i redux 或者yarn add redux

2.2 Store

//该文件用于暴露store对象
//引入createStore,专门用于创建redux中最核心的store对象
import {createStore} from 'redux'

//引入为count组件服务的reducer
import countReducer from './count_reducers'

//store中需要一个为之服务的reducer,因此将引入的reducer当参数传入这个创建的store中
//并且将这个store对象暴露出去
 export default createStore(countReducer)

2.3 reducer

//创建一个为组件服务的reducer,它的本质是一个纯函数
//定义个初始值
let initState=0
//传入两个参数,一个为之前的状态preState,另一个是动作对象action
//设置默认值,初始化数据data
//这里的reducer是一个纯函数
 export default function countReducers(preState =initState, action) {
    //从action中拿到type与data
    const {
        type,
        data
    } = action
//根据type加工数据
    switch (type) {
        case 'increment':
            return preState + data
        case 'decrement':
            return preState - data
        default:
            return preState
    }

}

2.4 action

//分别封装两个action函数用来创建action对象
export const createIncrement=data=>({type:"increment",data})
export const createDecrement=data=>({type:"decrement",data})

2.5 constant

//定义常量文件
export const INCREMENT='increment'
export const DECREMENT='decrement'

2.6具体使用

2.6.1 引入store

import store from '../../redux/count_redux/store'

2.6.2 通过getstate可以获取初始状态

import store from '../../redux/count_redux/store'

2.6.3 引入action

import {createIncrement,createDecrement} from '../../redux/count_redux/count_action'

2.6.4 使用dispatch去传递action

 //通知redux去加value
store.dispatch(createIncrement(value*1))

2.6.5 渲染render

//因为没有setstate,页面没有发生更新,需要监听redux,动态的去触发render函数
//使用setstate中去触发render
componentDidMount(){
    store.subscribe((()=>{
      this.setState({})
    }))
}
//这样修改每次都需要调用store.subscribe(),因此有如下方式
//重新渲染根组件,触发render
store.subscribe(()=>{
  ReactDOM.render(
      <App />,
    document.getElementById('root')
  );
})

2.6.6 案例

import React, { Component } from 'react'
// 引入store用于获取store状态
import store from '../../redux/count_redux/store'
import {createIncrement,createDecrement} from '../../redux/count_redux/count_action'
export default class index extends Component {
componentDidMount(){
    store.subscribe((()=>{
      this.setState({})
    }))
}
    //加
    increment=()=>{
  let {value}=this.selectNumber
  //通知redux去加value
store.dispatch(createIncrement(value*1))
  
    }
    //减
    decrement=()=>{
        let {value}=this.selectNumber
  //通知redux去加value
  store.dispatch(createDecrement(value*1))
       
    }
    //异步加
    asyncIcrement=()=>{
        let {value}=this.selectNumber
setTimeout(() => {
    store.dispatch(createIncrement(value*1))
}, 500);
    }
    //奇数加
    oddIcrement=()=>{
        let {value}=this.selectNumber
        let count=store.getState()
        if(count%2!==0){
            store.dispatch(createIncrement(value*1))
        }
    }
    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>
                <button onClick={this.increment}></button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.decrement}></button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.asyncIcrement}>异步加</button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.oddIcrement}>奇数加</button>&nbsp;&nbsp;&nbsp;
            </div>
        )
    }
}

3. 异步action

当我们需要在做一些定时器任务,发布订阅,网络请求就需要使用异步action,异步action是指它的值为一个函数,同步action是指它的值是一个对象

3.1 引入redux-thunk

yarn add redux-thunk

引入中间件,并且将中间件作为createStore的第二个参数传入,这里的第二个参数中间件是一个函数,可以传入一个参数为引入的thunk

//该文件用于暴露store对象
//引入createStore,专门用于创建redux中最核心的store对象
//引入applyMiddleware中间件
import {createStore,applyMiddleware} from 'redux'

//引入为count组件服务的reducer
import countReducer from './count_reducers'

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

//store中需要一个为之服务的reducer,因此将引入的reducer当参数传入这个创建的store中
//并且将这个store对象暴露出去
 export default createStore(countReducer,applyMiddleware(thunk))

3.2 异步处理过程

//在action中进行异步操作,返回一个函数,作为异步处理方案
//分别封装两个action函数用来创建action对象
import {
    INCREMENT,
    DECREMENT
} from './constant'
import store from './store'
export const createIncrement = data => ({
    type: INCREMENT,
    data
})
export const createDecrement = data => ({
    type: DECREMENT,
    data
})
//异步的action就是指action返回的是一个函数,并且该函数的返回值是一个同步的action
export const createAsyncIncrement = (data, time) => {
    return (dispatch) => {
        setTimeout(() => {
           dispatch(createIncrement(data))
        }, time);
    }
}

3.3 在组件中触发

   asyncIcrement=()=>{
        let {value}=this.selectNumber
    store.dispatch(createAsyncIncrement(value*1,500))
    }

4.React-redux

4.1 模型图

1615791605682-548a3915-bf92-47b9-a499-7df22b739afc[1].png

4.2 容器组件

1.在src目录下定义一个containers文件夹存放容器组件

2.在containers文件夹下新建一个math容器组件

3.容器组件需要借助react-redux

4.安装react-redux

yarn add react-redux

5.引入UI组件

// 引入math(ui组件)
import MathUI from '../../components/Math'

6.引入redux

//需要在ui组件使用的时候在标签上引入
//引入
import Math from './containers/math'
//使用
<Math store={store}/>

7.引入connect用于连接ui组件和redux

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

8.使用connect创建并暴露组件

//高阶函数,连接ui组件
//使用connect创建并暴露一个math组件
export default connect()(MathUI)

4.3 容器组件的使用案例

//container.jsx
// 引入math(ui组件)
import MathUI from '../../components/Math'

import{ createIncrement,createDecrement, createAsyncIncrement} from '../../redux/count_redux/count_action'
//引入store
// import store from '../../redux/count_redux/store'

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

// mapStateProps返回一个对象
// mapStateProps的返回值作为状态传递给UI组件
//返回对象有key,value,相当于props中的key和value
function mapStateProps(state){
    return{count:state}
}

//mapDispatchToProps返回一个对象
//mapDispatchToProps的返回值作为操作状态的方法传递给ui组件,
//key就是传递给ui组件中props中的key,value就是传递给ui组件的props中的value
function mapDispatchToProps(dispatch){
    return {
        jia:(data)=>{
            dispatch(createIncrement(data))
        },
        jian:(data)=>{
            dispatch(createDecrement(data))
        },
        jiaAsync:(data,time)=>{
            dispatch(createAsyncIncrement(data,time))
        }
    }
}
//高阶函数,连接ui组件
//使用connect创建并暴露一个math组件

//connect第一个函数是映射ui组件和容器组件的关系,第二个组件是连接ui组件和容器组件
export default connect(mapStateProps,mapDispatchToProps)(MathUI)
  • 1.这里的mapStateProp和mapDispatchToProps可以映射容器组件和ui组件。
  • 2.这里的容器组件就相当于ui组件的父组件,容器组件通过mapStateProp来传递状态到ui组件。
  • 3.这里的mapStateProp函数可以接受到一个参数,这个参数是state获取redux的初始状态,等价于redux中的store.getState(),并且返回一个对象,key就是传递给ui组件的props的key,value就是传递给ui组件的props的value。
  • 4.这里的mapDispatchToProps作为操作状态的方法,在ui组件中可以使用this.props。xxx()调用,mapDispatchToProps的参数是dispatch,用于将action传递给redux并且它返回一个对象,对象的key作为方法名,value是一个函数用于处理初始状态。
//在组件中
import React, { Component } from 'react'
export default class index extends Component {
componentDidMount(){
  
}
    //加
    increment=()=>{
  let {value}=this.selectNumber
   this.props.jia(value*1)
    }
    //减
    decrement=()=>{
        let {value}=this.selectNumber
        this.props.jian(value*1)
       
    }
    //异步加
    asyncIcrement=()=>{
        let {value}=this.selectNumber
        this.props.jiaAsync(value*1,500)
    }
    //奇数加
    oddIcrement=()=>{
        let {value}=this.selectNumber
        if(this.props.count%2!=0){
            this.props.jia(value*1)
        }
    }
    render() {
        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>
                <button onClick={this.increment}></button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.decrement}></button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.asyncIcrement}>异步加</button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.oddIcrement}>奇数加</button>&nbsp;&nbsp;&nbsp;
            </div>
        )
    }
}

4.4 容器组件的简化写法

// 引入math(ui组件)
import MathUI from '../../components/Math'

import{ createIncrement,createDecrement, createAsyncIncrement} from '../../redux/count_redux/count_action'
//引入store
// import store from '../../redux/count_redux/store'

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


//高阶函数,连接ui组件
//使用connect创建并暴露一个math组件

//connect第一个函数是映射ui组件和容器组件的关系,第二个组件是连接ui组件和容器组件
export default connect(
    state=>({
        count:state
    })
    //在react-redux中可以将action自动dispatch给redux,
    //如果两个参数都是函数,第二个参数可以写成一个对象
    ,{
    jia:createIncrement,
    jian:createDecrement,
    jiaAsync:createAsyncIncrement
})(MathUI)

5. provider的使用

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import store from './redux/count_redux/store'

  ReactDOM.render(
    // React.StrictMode检查app和app包裹的组件是否合理
    <BrowserRouter>
 <Provider store={store}>
 <App />
 </Provider>  
    </BrowserRouter>,
    ddocument.getElementById('root')  );

在index.js中使用provide,provide上直接使用store,容器组件得到store,避免多个ui组件上调用store,不然就需要逐一的在ui组件标签上引入store,例如如下:

import Login from './components/Login'
import Register from "./components/Register"
import store from './redux/store'
function App() {
  return (
    <div>
      <Login store={store}/>
      <hr/>
      <Register store={store}/>
    </div>
  );
}

export default App;

6.redux开发工具的使用

1.在谷歌浏览器上安装

2.在项目安装

yarn add redux-devtools-extension

3.在store文件夹中引入

import  {composeWithDevTools}  from 'redux-devtools-extension'
//传递到创建store的第二个参数,并将中间件applyMiddleware作为参数
 export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

4.成功安装如图

1616646039756-a68c7d0f-ad62-45fc-b931-5da34291dd73[1].png

7.React-redux开发案例

7.1 store

1.需要将多个reducer和并放在一个对象中
2.引入combineReducers
3.通过调用combineReducers,入参一个对象,这里的key可以自己命名,value分别是引入的reducer
//该文件用于暴露store对象
//引入createStore,专门用于创建redux中最核心的store对象
//引入applyMiddleware中间件
import {createStore,applyMiddleware,combineReducers} from 'redux'

//引入为count组件服务的reducer
import countReducer from './reducers/count'

import personReducer from './reducers/person'
//引入react-thunk,用于支持异步action
import thunk from 'redux-thunk'

//汇总reducer变成一个总的reducer
const allReducer=combineReducers({
    he:countReducer,
    rens:personReducer
})
//store中需要一个为之服务的reducer,因此将引入的reducer当参数传入这个创建的store中
//并且将这个store对象暴露出去
//
 export default createStore(allReducer,applyMiddleware(thunk

7.2 Action

创建一个对象,有两个参数,第一个参数是type,第二个参数是需要传递过来的数据data

7.2.1 count_action

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

})

export const createDecrement=data=>(
    {
        type:DECREMENT,
        data
    }
)

//异步action
export const createAsyncIncrement=(data,time)=>{
    return dispatch=>{
        setTimeout(() => {
            dispatch(incrementAction(data))
        }, time);
    }
}

7.2.2 person_action

import{ADD_PERSON}from '../constant'

export const createPerson=personObj=>({
   type:ADD_PERSON,
   data:personObj
})

7.3 Constant

存放常量文件

//暴露加法和减法
export const INCREMENT='increment'
export const DECREMENT='decrement'

export const ADD_PERSON='add_crement'

7.4 Reducers

7.4.1 count_reducer

  • 做一些逻辑处理,这里的函数接受两个参数,第一个参数是之前的值,第二个参数是外界传递过来的action,
  • action是一个对象,对象的参数是type与data
import { INCREMENT, DECREMENT} from '../constant'
const initState=0
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
    }

}

7.4.2 person_reducer

import{ADD_PERSON}from '../constant'

let person=[{id:"001",name:"ws",age:23}]

export default function PersonSum(preState=person,action){
    let {type,data}=action
    switch (type) {
        case ADD_PERSON:
           return [data,...preState]
    
        default:
           return preState
    }

7.5 组件内的使用

这里的组件有两个,一个是ui组件一个是容器组件,ui组件主要进行展示,容器组件做一些数据加工,容器组件包裹ui组件,并将容器组件暴露出去

7.5.1 Login Comoponents


import{ createIncrement,createDecrement, createAsyncIncrement} from '../../redux/actions/count'
//引入store
// import store from '../../redux/count_redux/store'

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

import React, { Component } from 'react'

 class index extends Component {
//加
    increment=()=>{
  let {value}=this.selectNumber
   this.props.jia(value*1)
    }
    decrement=()=>{
        let {value}=this.selectNumber
        this.props.jian(value*1)
       
    }
    //异步加
    asyncIcrement=()=>{
        let {value}=this.selectNumber
        this.props.jiaAsync(value*1,500)
    }
    //奇数加
    oddIcrement=()=>{
        let {value}=this.selectNumber
        if(this.props.count%2!=0){
            this.props.jia(value*1)
        }
    }
    render() {
        return (
            <div>
            <h1>person组件下方总人数{this.props.renshu}</h1>
                <h2>当前显示的数是:{this.props.count}</h2>
                <select ref={c=>this.selectNumber=c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
                <button onClick={this.increment}></button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.decrement}></button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.asyncIcrement}>异步加</button>&nbsp;&nbsp;&nbsp;
                <button onClick={this.oddIcrement}>奇数加</button>&nbsp;&nbsp;&nbsp;
            </div>
        )
    }
}

//高阶函数,连接ui组件
//使用connect创建并暴露一个math组件

//connect第一个函数是映射ui组件和容器组件的关系,第二个组件是连接ui组件和容器组件
export default connect(
    state=>({
        count:state.he,
        renshu:state.rens.length
    })
    //在react-redux中可以将action自动dispatch给redux,
    //如果两个参数都是函数,第二个参数可以写成一个对象
    ,{
    jia:createIncrement,
    jian:createDecrement,
    jiaAsync:createAsyncIncrement
})(index)

容器组件主要是通过connect进行使用

  • 1.首先需要下载react-redux,yarn add react-redux
  • 2.引入connect
  • 3.connect中可以调用两次,第一次调用可以传递两个函数,第一个函数返回一个对象,对象的值可以通过state.去获取reducer的值,第二个函数返回一个对象,key是我们自己定义的,value是函数。
  • 4.这里的函数可以是处理数据的action,action需要引入进来
  • 5.connect的第二次调用需要将ui组件传递进去

7.5.2 Register Components

下载nanoid ,生成一个随机且不重复的id

yarn add nanoid 或者 npm i nanoid

import React, { Component } from 'react'
import {nanoid} from 'nanoid'
import {connect} from 'react-redux'
import {createPerson}from "../../redux/actions/person"
 class index extends Component {
    addClick=()=>{
     const nameValue=this.nameNode.value
     const ageValue=this.ageNode.value
     const personObj={id:nanoid(),name:nameValue,age:ageValue}
     this.props.jiayiren(personObj)
     this.nameNode.value=""
     this.ageNode.value=""
    }
    render() {
        console.log(this.props.yiduiren,"gggg")
        return (
            <div>
            <h1>上方显示的和为{this.props.sum}</h1>
                <input type="text" placeholder="请输入姓名" ref={c=>this.nameNode=c}/>
                <input type="text" placeholder="请输入年龄" ref={c=>this.ageNode=c}/>
                <button onClick={this.addClick}>添加</button> 
              <ul>
               {
                   this.props.yiduren.map(p=>{
                       return <li key={p.id}>姓名:{p.name}===年龄:{p.age}</li>
                   })
               }
              </ul>
            </div>
        )
    }
}

export default connect(
    state=>(
        {
        yiduren:state.rens,
        sum:state.he

    }),{
        jiayiren:createPerson
    }
)(index)

7.5.3 index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import App from './App';
import store from './redux/store'



  ReactDOM.render(
    // React.StrictMode检查app和app包裹的组件是否合理
   
 <Provider store={store}>
 <App />
 </Provider>  
,
    document.getElementById('root')
  );

7.5.4 app.js

import Login from './components/Login'
import Register from "./components/Register"
function App() {
  return (
    <div>
      <Login/>
      <hr/>
      <Register></Register>
    </div>
  );
}

export default App;