• 有问题请联系QQ:2374146085
  • 有问题请联系QQ:2374146085

React-Redux

6个月前 (10-06) 406次浏览 已收录 3个评论 扫描二维码

导读

redux、react-redux、action、reducer、store、容器组件、Provider、多组件数据共享

 

redux基础概念

原理图

只负责管理状态.不负责更新视图.

 

redux的三个核心概念

action

  • (动作)值为对象(同步action)
    • type:标识属性, 值为字符串, 唯一, 必要属性
    • data:数据属性, 值类型任意, 可选属性
    • 例子:{ type: ‘ADD_STUDENT’,data:{name:’wxl’,age:18} }
  • 函数(异步action)
  • 但是最终Store只接收Object的action,如果action为函数,那么Store会去执行这个函数,而不是传给Reducres

reducer

  • 用于初始化状态、加工状态。
  • 加工时,根据旧的state和action,产生新的state的纯函数

store

  • 将state、action、reducer联系在一起的对象
  • 如何得到此对象
    • import {createStore} from ‘redux’
    • import countReducer from ‘./count_reducer.js’
    • const store = createStore(countReducer)
  • 此对象的功能
    • getState(): 得到state
    • dispatch(action): 分发action, 触发reducer调用, 产生新的state
    • subscribe(listener): 注册监听, 当产生了新的state时, 自动调用,可以在这里更新视图

示例

count组件

import React, { Component } from 'react'

import store from '../../redux/store'
//引入actionCreator 专门用于创建action对象
import {
    createIncrementAction , 
    createDecrementAction ,
    createIncrementAsyncAction
    } from '../../redux/count_action'

export default class index extends Component {

  //加法
  increment = ()=>{
    const {value} = this.selectNumber //获取下拉框选中的值
    //通知redux加上这个value
    // store.dispatch({type:'increment',data:parseInt(value)})
    store.dispatch(createIncrementAction(parseInt(value)))
}
//减法
decrement = ()=>{
    const {value} = this.selectNumber
    store.dispatch(createDecrementAction(parseInt(value)))
}
//store存的数为奇数再加
incrementIfOdd = ()=>{
    const {value} = this.selectNumber
    const count = store.getState()
    if(count%2!==0){
        store.dispatch(createIncrementAction(parseInt(value)))
    }
}
//异步加
incrementAsync = ()=>{
    const {value} = this.selectNumber

    // 异步action也可以直接写在组件里,异步调用同步action,即使用对象方式
    // setTimeout(()=>{
    //     store.dispatch(createIncrementAction(parseInt(value)))
    // },1000)

    //也可以真正使用异步action,不过需要redux-thunk
    store.dispatch(createIncrementAsyncAction(parseInt(value)))
  }
  render() {
    return (
      <div>
        <h1>当前求和:{store.getState()}</h1>
        <select name="" id="" ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
          <option value="4">4</option>
        </select>
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
        <button onClick={this.incrementIfOdd}>当前求和为奇数加</button>
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    )
  }
}

store.js

import {createStore , applyMiddleware} from 'redux'

//引入为Count组件服务的reducer
import countReducer from './count_reducer.js'

//引入redux-thunk 用于支持异步action,需要applyMiddleware执行这个中间件
import thunk from 'redux-thunk'

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

export default store //整个应用只有一个store对象

常量映射表constant.js

// 该文件用于定义action对象中type类型的常量值
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

count_action.js

// 专门为count组件生成action对象

import { INCREMENT,DECREMENT } from "./constant"

//同步action,action是个对象
export function createIncrementAction(data){
    return {type:INCREMENT,data:data}
}
export function createDecrementAction(data){
    return {type:DECREMENT,data:data}
}

//异步action,action是个函数,异步action中一般都会调用同步action去真正操作数据,类似vuex4中的act与mut
//异步action也可以直接写在组件里,异步调用同步action,即使用对象方式
export function createIncrementAsyncAction(data){
    return (dispatch)=>{
        setTimeout(()=>{
            // 通知redux改变store
            dispatch(createIncrementAction(data))
        },1000)
    }
}

count_reducer.js

//专门为count组件服务的reducer,本质就是一个函数

import { INCREMENT,DECREMENT } from "./constant"

/**
 * 
 * @param {*} preState 之前的对象
 * @param {*} aciton  动作对象
 */
const initState = 0 //初始化值
// store.js一加载就自己调用了counntReducer
export default function countReducer(preState = initState , action){
    const {type,data} = action
    switch(type){
        case INCREMENT:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            //没有任何动作,以及不给data(undefined),就代表初始化
            return preState
    }
}

index.js(main.js)

import React from 'react'
import ReactDOM from 'react-dom/client'
import {BrowserRouter} from 'react-router-dom'

import store from './redux/store.js'

import App from './App.jsx'

const root = ReactDOM.createRoot(document.getElementById('root'))

root.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>
)
  
store.subscribe(()=>{
    // 检测redux中状态的变化,只要变化就重新调用一次App组件,子组件就会更新视图
 root.render(<App/>)
})

react-redux

基本使用

  • 所有的UI组件都应该包裹着一个容器组件,他们是父子关系
  • 容器组件是真正和redux打交道的,容器组件可以任意使用redux的api
  • UI组件中不嫩使用任何redux的api
  • 容器组件会通过props传递给UI组件:
    • redux中所保存的状态
    • 用于操作状态的方法
  • 容器组件需要react-redux来创建

 

使用react-redux修改上节实例(没有修改上节redux相关代码,并且下方代码会用到上节的redux代码)

Count容器组件

import {
    createIncrementAction,
    createDecrementAction,
    createIncrementAsyncAction
} from '../../redux/count_action'

//引入Count的UI组件
import CountUI from '../../components/Count'

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

//使用connect()()创建一个Count组件的容器组件,并连接UI组件
// 传递第一个参数映射状态;第二个参数映射操作状态的方法
const Count = connect( mapStateToProps,mapDispatchToProps )( CountUI )
export default Count


//state映射为props的 函数 ,返回值对象作为 映射状态(redux),传递给了UI组件CountUI,其实是通过props的模式传递的
function mapStateToProps(state){ //state就属于redux里的状态,react-redux调用这个函数的时候,会把state传过来
    return {count:state}
}
//dispatch映射为props的 函数 ,返回值对象 作为 映射操作状态的方法,传递给了UI组件CountUI,其实是通过props的模式传递的
function mapDispatchToProps(dispatch){
    return {
        //第一种方式function
        add:(data)=>{
            // 通知redux执行加法
            dispatch(createIncrementAction(data))
        },
        jian:data => dispatch(createDecrementAction(data)),
        addAsync:data=>dispatch(createIncrementAsyncAction(data))


        //第二种方式:对象,需要作为对象直接放在上方connext函数里
        // 并且,react-redux会自动分发dispatch
        // add:createIncrementAction,
        // jian:createDecrementAction,
        // addAsync:createIncrementAsyncAction
    }
}

渲染容器组件

import React, { Component } from 'react'

import store from './redux/store'

// import Count from './components/Count'  //在react-reux里不能直接用普通组件
import Count from './containers/Count' //必须使用容器组件,因为容器组件一渲染,包裹的ui组件也会渲染了

export default class App extends Component {
  render() {
    return (
      <div>
        {/* 这样就把容器组件与redux(store)连接在一起 ,通过props传递*/}
        <Count store={store}></Count>
      </div>
    )
  }
}

CountUI组件

import React, { Component } from 'react'

export default class index extends Component {
  //加法
  increment = () => {
    const { value } = this.selectNumber //获取下拉框选中的值
    this.props.add(parseInt(value))
  }
  //减法
  decrement = () => {
    const { value } = this.selectNumber
    this.props.jian(parseInt(value))
  }
  //store存的数为奇数再加
  incrementIfOdd = () => {
    const { value } = this.selectNumber
    if(this.props.count % 2 !== 0){
      this.props.add(parseInt(value))
    }
  }
  //异步加
  incrementAsync = () => {
    const { value } = this.selectNumber
    this.props.addAsync(parseInt(value))
  }
  render() {
    console.log('react-redux-props',this.props)
    return (
      <div>
        <h1>当前求和:{this.props.count}</h1>
        <select name="" id="" ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
          <option value="4">4</option>
        </select>
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
        <button onClick={this.incrementIfOdd}>当前求和为奇数加</button>
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    )
  }
}

由于使用react-redux,所以不需要再在index.js(main.js)检测redux中的变化了,容器组件会自己检测

优化1:  借助Provider

未优化:

App组件

import React, { Component } from 'react'

import store from './redux/store'

// import Count from './components/Count'  //在react-reux里不能直接用普通组件
import Count from './containers/Count' //必须使用容器组件,因为容器组件一渲染,包裹的ui组件也会渲染了

export default class App extends Component {
  render() {
    return (
      <div>
        {/* 这样就把容器组件与redux(store)连接在一起 ,通过props传递*/}
        <Count store={store}></Count>
        <Count2 store={store}></Count2>
        <Count3 store={store}></Count3>
        <Count4 store={store}></Count4>
        <Count5 store={store}></Count5>
        <Count6 store={store}></Count6>
        <Count7 store={store}></Count7>
        <Count8 store={store}></Count8>
      </div>
    )
  }
}

使用provier优化后:

App组件

import React, { Component } from 'react'

// import Count from './components/Count'  //在react-reux里不能直接用普通组件
import Count from './containers/Count' //必须使用容器组件,因为容器组件一渲染,包裹的ui组件也会渲染了

export default class App extends Component {
  render() {
    return (
      <div>
        <Count></Count>
        <Count2></Count2>
        <Count3></Count3>
        <Count4></Count4>
        <Count5></Count5>
      </div>
    )
  }
}

index.js(main.js)

import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'

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

import App from './App.jsx'

const root = ReactDOM.createRoot(document.getElementById('root'))

root.render(
  <BrowserRouter>
    {/* 整个App应用(所有后代组件)里面,但凡需要store的 容器组件 ,Provider都能把store给传递这些组件 */}
    <Provider store={store}>
      <App />
    </Provider>
  </BrowserRouter>
)

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

  • 当一个组件需要redux的时候,那么直接把它创建成容器组件containers,并且在该文件里直接创建UI组件,然后原地使用.
  • 当一个组件不需要redux的时候,那么就把它创建成普通组件components
  • 当一个组件是路由组件的时候,那么就把它创建成路由组件views

例如UI组件和容器组件的整合:

import {
    createIncrementAction,
    createDecrementAction,
    createIncrementAsyncAction
} from '../../redux/count_action'


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

import React, { Component } from 'react'

//引入Count的UI组件,(不推荐)
// import CountUI from '../../components/Count'
//在容器组件里直接定义UI组件
class CountUI extends Component {
    //加法
    increment = () => {
      const { value } = this.selectNumber //获取下拉框选中的值
      this.props.add(parseInt(value))
    }
    //减法
    decrement = () => {
      const { value } = this.selectNumber
      this.props.jian(parseInt(value))
    }
    //store存的数为奇数再加
    incrementIfOdd = () => {
      const { value } = this.selectNumber
      if(this.props.count % 2 !== 0){
        this.props.add(parseInt(value))
      }
    }
    //异步加
    incrementAsync = () => {
      const { value } = this.selectNumber
      this.props.addAsync(parseInt(value))
    }
    render() {
      console.log('react-redux-props',this.props)
      return (
        <div>
          <h1>当前求和:{this.props.count}</h1>
          <select name="" id="" ref={(c) => (this.selectNumber = c)}>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
          </select>
          <button onClick={this.increment}>+</button>
          <button onClick={this.decrement}>-</button>
          <button onClick={this.incrementIfOdd}>当前求和为奇数加</button>
          <button onClick={this.incrementAsync}>异步加</button>
        </div>
      )
    }
  }


//使用connect()()创建一个Count组件的容器组件,并连接UI组件
// 传递第一个参数映射状态;第二个参数映射操作状态的方法
const Count = connect( mapStateToProps,mapDispatchToProps )( CountUI )
export default Count


//state映射为props的 函数 ,返回值对象作为 映射状态(redux),传递给了UI组件CountUI,其实是通过props的模式传递的
function mapStateToProps(state){ //state就属于redux里的状态,react-redux调用这个函数的时候,会把state传过来
    return {count:state}
}
//dispatch映射为props的 函数 ,返回值对象 作为 映射操作状态的方法,传递给了UI组件CountUI,其实是通过props的模式传递的
function mapDispatchToProps(dispatch){
    return {
        //第一种方式function
        add:(data)=>{
            // 通知redux执行加法
            dispatch(createIncrementAction(data))
        },
        jian:data => dispatch(createDecrementAction(data)),
        addAsync:data=>dispatch(createIncrementAsyncAction(data))
    }
}

组件与redux打通的步骤

  • A文件里定义好UI组件,不暴露
  • A文件里通过引入connect生成一个容器组件,并暴露,写法如下:
export default connect(
    state=>({key:value}),//映射状态
    {key:xxxAction} //映射操作状态的方法
)(UI)
  • A文件里,在UI组件中通过this.props.xxxxx读取和操作状态

数据共享(多个组件)

共享组件的reducer要使用combineReducers进行合并,合并后的总状态是一个对象state(总).

hooks(未完善)

useSelector()

reduxstore对象中获取数据(state)

useDispatch()

store中对dispatch函数的引用

 


渣渣龙, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:React-Redux
喜欢 (1)

您必须 登录 才能发表评论!

(3)个小伙伴在吐槽
  1. 6666666
    百事2022-10-08 20:50
  2. 666666666
    百事2022-10-08 20:51
  3. 6666
    百事2022-10-08 20:51