React教程详解三(Redux与React-Redux)

  • Post author:
  • Post category:其他


Redux

Redux是JavaScript应用的状态容器,提供可预测的状态管理,功能与vueX一样;上述脚手架并未自动安装上redux,需自行安装~

npm install redux

注意:Redux并不是只为react框架提供状态管理功能,它也支持其它前端框架,并不是由Facebook打造的产品~

通过使用Redux,可以集中式存储和管理应用的状态;在处理组件通信问题时,可以无视组件之间的层级关系;

它有三个核心概念:action、store以及reducer;


action、store与reducer

  • action

见名之意,action用于定义动作,它其实

就是一个包含type以及data属性的js对象,

其中type属性属于标识属性,用于区分动作的类型,而data属性为可选属性,表示本次动作携带的数据;action只用于描述要做什么,并不真正执行动作(完成响应功能);

{ type: 'increment' } 
{ type: 'decrement', data: 2 } 
{ type:  'addTodo', data: '吃饭' }
{ type: 'removeTodo', data: {name: '睡觉', done: false} }
  • store

见名之意,store表示仓库,它是Redux的核心功能,用于整合action以及reducer;

注意,一个应用(项目)中只有一个store!

store由于地位特殊,身上封装了很多操作功能的API:

store.getStore() // 获取维护的状态
const store = createStore(xxxreducer) // 创建store 要接收对应的reducer作为参数
store.dispatch(xxxaction) // 分发动作,进行状态更新 要接收指定action作为参数
store.subscribe(() => {}) // 订阅状态变化(监听状态变化)
  • reducer

reducer是真正执行action动作的地方,

它其实就是一个函数,用于初始化状态以及修改状态;

该函数接收两个参数(previousState, action),分别是旧状态和要执行的action,根据传入的不同action分别进行不同的处理,

返回不同的新状态值

。通常与switch case语句结合~

const initState = 10 // 初始化状态
const reducer = (preState = initState, action) => {
  const {type, data} = action
  switch (type) {
    case 'jia':
      // 返回新的state
      return preState + data
    case 'jian':
      // 返回新的state
      return preState - data
    default:
      return preState // 若不匹配任何动作,则为初始化状态
  }
}

可指定action动作的初始值,若不匹配任何action,则会执行初始化动作,可在reducer中进行定义~

Redux代码执行过程

①创建store

只要创建store,那么,Redux就会调用一次reducer(此时的type是一个随机值,类似于

@@redux/INIT5.s.d.l.5

的样式),此次调用reducer是为了给状态初始化(获取状态的默认值)



注意:若有多个状态管理,则要创建多个reducer,并利用




combineReducers




方法将多个reducer汇总传给store;

②更新状态

当需要更新状态时,首先要分发动作store.dispatch(action),随后调用euducer,计算出新的状态并返回;

③获取最新状态

React-Redux

React-Redux是redux官方react绑定库,专门用来简化react使用redux,安装脚手架时并未主动安装上react-redux,需单独安装,

一般与原生redux结合使用;

npm install react-redux

React-Redux将所有组件分为两大类:

UI组件和容器组件;


其中UI组件只负责页面的呈现、交互,不能使用redux中的任何API;


容器组件负责和redux通信,将数据传给UI组件;


容器组件包裹UI组件,组成父子组件;


容器组件会传给UI组件:①redux中所保存的状态 ②用来操作状态的方法;这两个都要通过props传递;


容器组件与UI组件形成父子关系的方法不是通过标签包裹实现的,而是通过react-redux中的connect函数实现的;

import { connect } from 'react-redux'
connect(mapStateToProps, mapDispatchToProps)(UI组件)
// mapStateToProps: 映射状态,返回值是一个对象
// mapDispatchToProps: 映射操作状态的方法,返回值是一个对象;

connet方法接收两个


函数


参数,第一个表示要传入子组件的state,第二个参数表示要传入子组件的操作状态的方法,返回值均为一个对象;

示例

下面通过四种方式将两兄弟组件(Count与Person组件)的通信方式进行比较:

通过prop传递事件进行通信

// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
     <App />
  </React.StrictMode>
);

// App.js组件
import { Component } from 'react';
import Count from './components/Count'
import Person from './components/Person'
import { nanoid } from 'nanoid';
import './App.css';

export default class App extends Component {
  state = {
    count: 0,
    person: []
  }
  addPerson = (name, age) => {
    const obj = {
      name: name,
      age: age,
      id: nanoid()
    }
    if (!name || !age) return
    this.setState({person: [...this.state.person, obj]})
  }
  increment = (val) => {
    this.setState({count: this.state.count + val * 1})
  }
  decrement = (val) => {
    this.setState({count: this.state.count - val * 1})
  }
  render() {
    return (
      <div className="App"> 
        <Count {...this.state} addPerson={this.addPerson} increment={this.increment} decrement={this.decrement}/>
        <hr/>
        <Person {...this.state} addPerson={this.addPerson} increment={this.increment} decrement={this.decrement}/>
      </div>
    )
  }
}
// Count.jsx组件
import React, { Component } from 'react'

export default class Count extends Component {
  render() {
    const {count, person} = this.props
    return (
      <div>
        <h1>这是Count组件</h1>
        <h2>Person组件的人数是:{person.length}</h2>
        <h2>求和值是{count}</h2>
        <select ref = {(ele) => {this.selectValue = ele}}>
          <option value="1">1</option>
          <option value="2">2</option>
        </select>
        <button onClick = {()=>{this.props.increment(this.selectValue.value)}}>加</button>
        <button onClick = {()=>{this.props.decrement(this.selectValue.value)}}>减</button>
      </div>
    )
  }
}
// Person.jsx组件
import React, { Component } from 'react'

export default class Person extends Component {
  render() {
    return (
      <div>
        <h1>这是Person组件</h1>
        <h2>Count组件的数字是:{this.props.count}</h2>
        <ul>
          {this.props.person.map((p) => {
            return <li key={p.id}>{p.name} --- {p.age}</li>
          })}
        </ul>
        <input ref={(ele) => {this.name = ele}} type="text" placeholder='请输入名称'/>
        <input ref={(ele) => {this.age = ele}} type="text" placeholder='请输入年龄'/>
        <button onClick={() => {this.props.addPerson(this.name.value,this.age.value)}}>添加</button>
      </div>
    )
  }
}

使用prop进行父子间通信较为方便,若是兄弟组件进行通信,则不仅要将状态通过prop传递,也要将事件通过prop进行传递(通过子组件调用父组件的方法),较为麻烦~

通过消息订阅方式

该方法通过安装PubSubJs库使用,首先在项目中进行安装:

npm install pubsub-js

并在项目中进行引入

import PubSub from 'pubsub-js'

该库主要通过

subscribe(订阅消息)和publish(发布消息)

方法进行使用;

const xxxPid = PubSub.subscribe('xxx消息', (msg, data) => {}) //  订阅消息,msg就是xxx消息,data是接收到的数据
PubSub.publish('xxx消息', data) //  发布xxx消息,data就是要进行发布的数据
PubSub.unsubscribe(xxxPid) // 取消订阅某消息
// index.js与上一方法相同
// App.js组件
import { Component } from 'react';
import Count from './components/Count'
import Person from './components/Person'
import './App.css';

export default class App extends Component {
  render() {
    return (
      <div className="App"> 
        <Count/>
        <hr/>
        <Person/>
      </div>
    )
  }
}
// Count.jsx组件
import React, { Component } from 'react'
import PubSub from 'pubsub-js'

export default class Count extends Component {
  state = {
    count: 0,
    length: 0
  }
  increment = (val) => {
    this.setState({count: this.state.count + val * 1})
    PubSub.publish('increment', this.state.count + val * 1) // 发布increment,并将数据传过去
  }
  decrement = (val) => {
    this.setState({count: this.state.count - val * 1})
    PubSub.publish('decrement', this.state.count - val * 1) // 发布decrement,并将数据传过去
  }
  componentDidMount() {
     // 订阅addPerson, 利用回调函数接收数据并进行处理
    this.add = PubSub.subscribe('addPerson', (msg, data) => {
      this.setState({length: data.length})
    })
  }
  componentWillUnmount() {
    PubSub.unsubscribe(this.token) // 取消订阅token
  }
  render() {
    return (
      <div>
        <h1>这是Count组件</h1>
        <h2>Person组件的人数是:{this.state.length}</h2>
        <h2>求和值是{this.state.count}</h2>
        <select ref = {(ele) => {this.selectValue = ele}}>
          <option value="1">1</option>
          <option value="2">2</option>
        </select>
        <button onClick = {()=>{this.increment(this.selectValue.value)}}>加</button>
        <button onClick = {()=>{this.decrement(this.selectValue.value)}}>减</button>
      </div>
    )
  }
}
// Person.jsx组件
import React, { Component } from 'react'
import { nanoid } from 'nanoid';
import PubSub from 'pubsub-js'

export default class Person extends Component {
  state = {
    count: 0,
    person: []
  }
  addPerson = (name, age) => {
    console.log(PubSub);
    const obj = {
      name: name,
      age: age,
      id: nanoid()
    }
    if (!name || !age) return
    this.setState({person: [...this.state.person, obj]})
    PubSub.publish('addPerson', [...this.state.person, obj])
  }
  componentDidMount() {
    this.jia = PubSub.subscribe('increment', (msg, data) => {
      // 这里用了箭头函数才能取到setState
      this.setState({count: data})
    })
    this.jian = PubSub.subscribe('decrement', (msg, data) => {
      // 这里用了箭头函数才能取到setState
      this.setState({count: data})
    })
  }
  componentWillUnmount() {
    PubSub.unsubscribe(this.jia) // 取消订阅jia
    PubSub.unsubscribe(this.jian) // 取消订阅jian
  }
  render() {
    return (
      <div>
        <h1>这是Person组件</h1>
        <h2>Count组件的数字是:{this.state.count}</h2>
        <ul>
          {this.state.person.map((p) => {
            return <li key={p.id}>{p.name} --- {p.age}</li>
          })}
        </ul>
        <input ref={(ele) => {this.name = ele}} type="text" placeholder='请输入名称'/>
        <input ref={(ele) => {this.age = ele}} type="text" placeholder='请输入年龄'/>
        <button onClick={() => {this.addPerson(this.name.value,this.age.value)}}>添加</button>
      </div>
    )
  }
}

补充:安装库的时候要注意是pubsub-js,而不是pubsub.js,虽都是消息订阅与发布库,但是传的参数是不一样的!

该方法为非官网出品,大型项目还是尽量少用~

状态管理之原生redux

// app.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals'; // 页面性能分析文件(需要web-vitals库的支持)
import store from './store/store'

const root = ReactDOM.createRoot(document.getElementById('root'));
// react.strictMode用于开启对app及其子组件的语法检查,不加也没啥
root.render(
  <React.StrictMode>
      <App />
  </React.StrictMode>
);
store.subscribe(() => {
  root.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
  );
})
// redux仅是进行状态管理,但是并不会对页面上的状态并不会主动更新,需要订阅store.subscribe
// 监视状态变化,若变化了则重新渲染页面


状态管理文件夹:

// store/store.js
// 该文件用于创建store,整个应用只有一个store对象
import { createStore } from 'redux'

import combineReducers from './reducer'

export default createStore(combineReducers) // 创建store
// store/reducer/count.js
// 该文件是为创建一个为count组件服务的reducer,reducer的本质是一个函数
// 它会接收到两个参数,第一个是previousState,另一个参数是action动作对象
const initState = 0 // 定义初始值
export default function countReducer(previousState = initState, action) {
  const {type, data} = action
  switch (type) {
    case 'increment':
      return previousState + data// 返回新数据
    case 'decrement':
      return previousState - data// 返回新数据
    default:
      return previousState // 若不匹配任何动作,则为初始化,返回初始值
  }
}
// store/reducer/person.js
const initState = []
export default (previousState = initState, action) => {
  const {type, data} = action
  switch (type) {
    case 'addPerson':
      return [...previousState, data]
    default:
      return previousState;
  }
}
// store/reducer/index.js
import { combineReducers } from 'redux'
import count from './count'
import person from './person'

export default combineReducers({count, person})
// store/action/count.js
export const incrementAction = (data) => ({type: 'increment', data}) // 返回一个对象
export const decrementAction = (data) => ({type: 'decrement', data}) // 返回一个对象
// store/actions/person.js
export const addPerson = (data) => ({type: 'addPerson', data}) // 返回一个对象


组件代码如下:

// components/Count.jsx
import React, { Component } from 'react'
import store from '../store/store'
import {incrementAction, decrementAction} from '../store/action/count'

export default class Count extends Component {
  increment = (val) => {
    store.dispatch(incrementAction(val * 1)) // 分发动作
  }
  decrement = (val) => {
    store.dispatch(decrementAction(val * 1)) // 分发动作
  }
  render() {
    return (
      <div>
        <h1>这是Count组件</h1>
        <h2>Person组件的人数是:{store.getState().person.length}</h2>
        <h2>求和值是{store.getState().count}</h2>
        <select ref = {(ele) => {this.selectValue = ele}}>
          <option value="1">1</option>
          <option value="2">2</option>
        </select>
        <button onClick = {()=>{this.increment(this.selectValue.value)}}>加</button>
        <button onClick = {()=>{this.decrement(this.selectValue.value)}}>减</button>
      </div>
    )
  }
}

// components/Person.jsx
import React, { Component } from 'react'
import { nanoid } from 'nanoid';
import store from '../store/store'
import { addPerson } from "../store/action/person";

export default class Person extends Component {
  addPerson = (name, age) => {
    const obj = {
      name: name,
      age: age,
      id: nanoid()
    }
    if (!name || !age) return
    store.dispatch(addPerson(obj))
  }
  render() {
    return (
      <div>
        <h1>这是Person组件</h1>
        <h2>Count组件的数字是:{store.getState().count}</h2>
        <ul>
          {store.getState().person.map((p) => {
            return <li key={p.id}>p.name---p.age</li>
          })}
        </ul>
        <input ref={(ele) => {this.name = ele}} type="text" placeholder='请输入名称'/>
        <input ref={(ele) => {this.age = ele}} type="text" placeholder='请输入年龄'/>
        <button onClick={() => {this.addPerson(this.name.value,this.age.value)}}>添加</button>
      </div>
    )
  }
}



使用原生redux方法需注意两点:

① redux并


不会主动监测


状态更新,所以状态更新后不会主动渲染页面,需要我们编写代码进行监测:一般在入口文件index.js文件中利用


store.subscribe


方法进行监测(发现改变立即重新渲染页面);

store.subscribe(() => {
  root.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
  );
})

②通过combineReducers将所有的reducer整合到一起,利用store进行管理,从而实现状态共享;

③在使用状态中数据时使用store.getState()方法拿到所有状态对象,根据情况取到正确的数据~

状态管理之react-redux

// App.jsx
import { Component } from 'react';
import Count from './components/Count'
import Person from './components/Person'
import './App.css';
import store from './store/store';

export default class App extends Component {
  render() {
    return (
      <div className="App">
        <Count store={store}/>
        <hr/>
        <Person store={store}/>
      </div>
    )
  }
}
// components/Count/index.jsx
import React, { Component } from 'react'
import {connect} from 'react-redux' // 引用connect
import {incrementAction, decrementAction} from '../../store/action/count'

class Count extends Component {
  increment = (val) => {
    this.props.sum(val * 1)
  }
  decrement = (val) => {
    this.props.sub(val * 1)
  }
  render() {
    return (
      <div>
        <h1>这是Count组件</h1>
        <h2>Person组件的人数是:{this.props.person.length}</h2>
        <h2>求和值是{this.props.count}</h2>
        <select ref = {(ele) => {this.selectValue = ele}}>
          <option value="1">1</option>
          <option value="2">2</option>
        </select>
        <button onClick = {()=>{this.increment(this.selectValue.value)}}>加</button>
        <button onClick = {()=>{this.decrement(this.selectValue.value)}}>减</button>
      </div>
    )
  }
}

function mapStateToProps(state) {
  return {
    count: state.count,
    person: state.person
  }
}
function mapDispatchToProps(dispatch) {
  return {
    sum: number => dispatch(incrementAction(number)), // 映射加的动作到props
    sub: number => dispatch(decrementAction(number)) // 映射减的动作到props
  }
}
// 利用connect将生成Count组件对应的容器组件,并暴露出去
export default connect(mapStateToProps,mapDispatchToProps)(Count)
// components/Person/index.jsx
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { nanoid } from 'nanoid';
import { addPerson } from '../../store/action/person';

class Person extends Component {
  addPerson = (name, age) => {
    const obj = {
      name: name,
      age: age,
      id: nanoid()
    }
    if (!name || !age) return
    this.props.addPerson(obj)
  }
  render() {
    return (
      <div>
        <h1>这是Person组件</h1>
        <h2>Count组件的数字是:{this.props.count}</h2>
        <ul>
          {this.props.person.map((p) => {
            return <li key={p.id}>{p.name}---{p.age}</li>
          })}
        </ul>
        <input ref={(ele) => {this.name = ele}} type="text" placeholder='请输入名称'/>
        <input ref={(ele) => {this.age = ele}} type="text" placeholder='请输入年龄'/>
        <button onClick={() => {this.addPerson(this.name.value,this.age.value)}}>添加</button>
      </div>
    )
  }
}
function mapStateToProps(state) {
  return {
    count: state.count,
    person: state.person
  }
}
function mapDispatchToProps(dispatch) {
  return {
    addPerson: data => dispatch(addPerson(data))
  }
}
export default connect(mapStateToProps,mapDispatchToProps)(Person)

// store/store.js
// 该文件用于创建store,整个应用只有一个store对象
import { createStore } from 'redux'

import combineReducers from './reducer'

export default createStore(combineReducers) // 创建store
// store/reducer/count.js
// 该文件是为创建一个为count组件服务的reducer,reducer的本质是一个函数
// 它会接收到两个参数,第一个是previousState,另一个参数是action动作对象
const initState = 0 // 定义初始值
export default function countReducer(previousState = initState, action) {
  const {type, data} = action
  switch (type) {
    case 'increment':
      return previousState + data// 返回新数据
    case 'decrement':
      return previousState - data// 返回新数据
    default:
      return previousState // 若不匹配任何动作,则为初始化,返回初始值
  }
}
// store/reducer/person.js
const initState = []
export default function personReducer(previousState = initState, action) {
  const {type, data} = action
  switch (type) {
    case 'addPerson':
      return [...previousState, data];
    default:
      return previousState;
  }
}
// store/reducer/index.js
// 该文件用于将所有的reducer进行汇总
import { combineReducers } from 'redux'
import person from './person'
import count from './count'

export default combineReducers({
  person,
  count
})
// store/action/count.js
export const incrementAction = (data) => ({type: 'increment', data}) // 返回一个对象
export const decrementAction = (data) => ({type: 'decrement', data}) // 返回一个对象
// store/action/person.js
export const addPerson = (data) => ({type: 'addPerson', data})


代码文件目录如下:

上述几个文件已可以实现所要功能,但有几个地方要进行详细探讨:

  • createStore

该函数接收reducer作为参数,用于为本次项目创建一个store,基于后续redux的操作皆是源于该store;


还要注意的一点是组件中的store对象并不是通过引用store文件拿到的,而是通过其父组件传过来的(本次示例是通过在App.js引入store文件,然后利用父子间同信传过去的)

  • 容器组件与UI组件

一般将容器组件与UI组件写到一个文件中,

暴露容器组件

  • 自动监视state的变化

原生redux只是对状态进行了处理,但是不负责检测状态改变,要自己通过渲染组件的方式重新获取当前状态,react-redux对这点进行了改进,使得用到状态的容器组件自动检测状态改变,从而渲染页面,使页面始终保持最新数据;

与原生redux不一样,react-redux可自动检测

  • 使用Provider

由于每个使用react-redux中的组件都要通过父组件拿到store,因此采用Provider组件将子组件包裹,并在Provider组件中传入store的方式较为方便些~(使用之前要先引入),一般采用在入口文件index.js中直接将App.js组件包裹的方式,是App.js的所有后代组件都能接收到store;

import { Provider } from 'react-redux';
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals'; // 页面性能分析文件(需要web-vitals库的支持)
import { Provider } from 'react-redux';
import store from './store/store'
import './index.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
// react.strictMode用于开启对app及其子组件的语法检查,不加也没啥
root.render(
  <React.StrictMode>
    <Provider store = {store}>
      <App />
    </Provider>
  </React.StrictMode>
  • connect(mapStateToProps, mapDispatchToProps)(UI组件)函数

connect函数中的两个


函数


参数分别映射redux中的状态和动作,


其实第二个参数也可以传入一个对象,


将对应的action动作指明即可,不需要传递参数,因为action中已经定义了参数,react-redux会自动识别对应哪个动作和传入的参数;

// components/Person/index.jsx
import React, { Component } from 'react'
import {connect} from 'react-redux'
import { nanoid } from 'nanoid';
import { addPerson } from '../../store/action/person'

class Person extends Component {
  addPerson = (name, age) => {
    const obj = {
      name: name,
      age: age,
      id: nanoid()
    }
    if (!name || !age) return
    this.props.addPerson(obj)
  }
  render() {
    return (
      <div>
        <h1>这是Person组件</h1>
        <h2>Count组件的数字是:{this.props.count}</h2>
        <ul>
          {this.props.person.map((p) => {
            return <li key={p.id}>{p.name}---{p.age}</li>
          })}
        </ul>
        <input ref={(ele) => {this.name = ele}} type="text" placeholder='请输入名称'/>
        <input ref={(ele) => {this.age = ele}} type="text" placeholder='请输入年龄'/>
        <button onClick={() => {this.addPerson(this.name.value,this.age.value)}}>添加</button>
      </div>
    )
  }
}

export default connect(
  (state) => ({count: state.count, person: state.person}),
  {addPerson: addPerson}, // 动作映射可以写成一个对象
  )(Person)

// components/Count/index.jsx
import React, { Component } from 'react'
import {connect} from 'react-redux' // 引用connect
import {incrementAction, decrementAction} from '../../store/action/count'

class Count extends Component {
  increment = (val) => {
    this.props.sum(val * 1)
  }
  decrement = (val) => {
    this.props.sub(val * 1)
  }
  incrementAsync = (val) => {
    setTimeout(() => {
      this.props.sum(val * 1)
    }, 1000)
  }
  render() {
    return (
      <div>
        <h1>这是Count组件</h1>
        <h2>Person组件的人数是:{this.props.person.length}</h2>
        <h2>求和值是{this.props.count}</h2>
        <select ref = {(ele) => {this.selectValue = ele}}>
          <option value="1">1</option>
          <option value="2">2</option>
        </select>
        <button onClick = {()=>{this.increment(this.selectValue.value)}}>加</button>
        <button onClick = {()=>{this.decrement(this.selectValue.value)}}>减</button>
        <button onClick = {()=>{this.incrementAsync(this.selectValue.value)}}>异步加</button>
      </div>
    )
  }
}

// 利用connect将生成Count组件对应的容器组件,并暴露出去
export default connect(
  (state) => ({
    count: state.count,
    person: state.person
  }),
  {
    sum: incrementAction, 
    sub: decrementAction
  }, // 传入要映射到props中的动作
  )(Count)

扩展:箭头函数若只返回一个对象,则可以在外侧包一个括号,则返回为一个函数,如本例中:

(state) => ({
    count: state.count,
    person: state.person
})
  • combineReducers({})函数

该函数用于整合所有的reducer,将整合后的reducer传给createStore方法,合并后的总状态变成了一个对象,则状态管理store中就会包含多个状态,从而实现数据共享;

如本例中store状态中包含count和person两个数据,使用时要取对值~

纯函数

纯函数是一类特殊的函数,最大的特点是:


对于同样的输入,必定得到同样的输出


它遵守一些约定:

① 不得改写参数数据

② 不会产生任何副作用,如网络请求,输入和输出设备

③ 不能调用Date.now()或者Math.random()等方法



必须保证redux的reducer函数必须是一个纯函数!

基于此,所以本例中对于person的reducer并没有采用数组的push或unshift方法,因为它改变了参数数据(数组的push、unshift方法会改变原数组,即改变参数previousState):

const initState = []
export default function personReducer(previousState = initState, action) {
  const {type, data} = action
  switch (type) {
    case 'addPerson':
      return [...previousState, data]; // 此处并没有使用数据的方法
    default:
      return previousState;
  }
}

异步action的处理

一般对于异步action的处理,可以通过在组件中定义好逻辑,然后调用同步action,如:

// components/Count/index.jsx
import React, { Component } from 'react'
import {connect} from 'react-redux' // 引用connect
import {incrementAction} from '../../store/action/count'

class Count extends Component {
  incrementAsync = (val) => {
    setTimeout(() => { // 写异步逻辑
      this.props.sum(val * 1) // 调用同步action
    }, 1000)
  }
  render() {
    return (
      <div>
        <h1>这是Count组件</h1>
        <h2>Person组件的人数是:{this.props.person.length}</h2>
        <h2>求和值是{this.props.count}</h2>
        <select ref = {(ele) => {this.selectValue = ele}}>
          <option value="1">1</option>
          <option value="2">2</option>
        </select>
        <button onClick = {()=>{this.incrementAsync(this.selectValue.value)}}>异步加</button>
      </div>
    )
  }
}

// 利用connect将生成Count组件对应的容器组件,并暴露出去
export default connect(
  (state) => ({
    count: state.count,
  }),
  {
    sum: incrementAction, 
  }, // 传入要映射到props中的动作
  )(Count)

当然,也可以通过直接在action中定义异步方法实现,此时的


action返回的是一个函数,该函数自带有dispatch这个参数~

需要安装redux-thunk库配合实现;

npm install redux-thunk

在store.js文件中引入redux-thunk以及

applyMiddleware(中间件)

// 该文件用于创建store,整个应用只有一个store对象
import { createStore, applyMiddleware } from 'redux'

import combineReducers from './reducer'
import thunk from 'redux-thunk' // 引入redux-thunk用于实现异步操作

export default createStore(combineReducers, applyMiddleware(thunk))

在action中封装异步操作,一般是写完异步逻辑后,调用同步action

export const incrementAction = (data) => ({type: 'increment', data}) // 返回一个对象
export const decrementAction = (data) => ({type: 'decrement', data}) // 返回一个对象
export const incrementAsync = (data) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(incrementAction(data))
    }, 500)
  }
} // 返回一个对象

其余逻辑与上面讲的相同,可自行编写~

此种异步方法可酌情使用(反正我不想这样用,麻烦哈哈哈哈)~

Redux与React-Redux的区别

react-redux利用容器组件与ui组件的父子关系实现数据和方法的使用;

原生redux确实使用原生store.getState()和store.dispatch()方法实现数据和方法的使用(哈哈哈,像是废话~)

redux开发工具的使用

需安装插件Redux-DevTools,配合redux-devtools-extention库使用;

npm install redux-devtools-extention

并配置于创建store的store.js文件中,

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

const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

则启动项目后按F12可看到有Redux选项~



恭喜你看到了这里,又get到一项知识~bye


更多前端知识欢迎来首页查看哦~~



版权声明:本文为ks795820原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。