小白路程之----初学React语法栈之redux与react-redux

981 阅读7分钟

Redux

1、核心概念

    Redux的主要目的,即集中且可预测的管理数据。集中管理指:将所有需要管理的数据放到一个store中进行管理。可预测指:数据的变化可以预测。为了实现这个功能,Redux将数据的操作全部交给了reducer来管理,而在action中定义数据处理的动作。这样当你的数据处理动作action传过来时,store便已经知道将要对数据进行的处理。在Redux中主要有三个内容,分别是store,action以及reducer。

  • 1、store:连接reducer和state,并且为处理数据提供接口,
  • 2、action:描述对数据处理的动作,是store 数据的唯一来源
  • 3、reducer:接收 state 和 action,并返回新的 state 的函数。

三大原则

  • 单一数据源:整个应用的state被储存在一棵object tree中,并且object tree只存在于唯一一个store中。

避免数据被修改,以及确定唯一的修改数据的方法就是访问store。

  • State 是只读的:唯一改变state的方法就是触发action,action是一个用于描述已发生事件的普通对象。

对store外部来说,只能访问state数据,而不能修改,确保state不被其他的方法修改,而所有的数据修改方法都被reducer集中化处理。

  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree,你需要编写 reducers。

action描述处理数据这件事,对数据的处理是在reducer内完成的,接收先前的state以及action,并返回新的state。

使用Redux的步骤

以counter计数器为例,

1、创建state对象

    对应第一个原则,单一数据源,在Redux中所有的对象都被保存在一个单一的对象中,

在store.js文件中先定义所需的对象,如下:

const state = {
    counter:99,
}

2、定义action

  • 官网:Action是把数据从应用传到store的有效载荷。它是store数据的唯一来源。一般来说你会通过store.dispatch()将action传到store。
  • 简单来说就是要求store处理数据的信号,对于不同的信号reducer对数据进行不同的处理。

实现加减操作,定义如下:

//实现加的increment操作:
const increment ={
    type = 'INCREMENT'
}
// 减操作的action
const decrement = {
    type: 'DECREMENT'
}
  • action创建函数,就是创建action的方法,只是简单的返回一个action。 如下,定义的increment()函数,返回的就是简单的一个action,方法内的text可以定义每次修改的数值的多少。
//返回加操作的action
function increment(text) {
    return {
        type: 'INCREMENT',
        text
  }
}
// 减操作decrement的action
function decrement() {
    return {
        type: 'DECREMENT',
    }
}

3、定义reducer

  • reducer的职责就是根据不同的action来处理数据。根据第三条规则,使用纯函数来执行修改。

对于纯函数必须遵守以下约束。

  • 不得改写传入的参数
  • 不能调用系统I/O的API(如DOM操作、http请求)
  • 不能调用不纯的方法(如Date.now()或者Math.random())。

对于计数器案例,定义的reducer如下,

function reducer(state = initState, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + action.text }
        case 'DECRMENT':
            return { count: state.count - 1 }
        default:
            return state
    }
}

需要注意

  • 不能修改state,
  • 在default的情况下返回旧的state

当数据量多时需要对数据进行拆分处理,可以定义多个reducer,然后进行合并。

function reducer(state = state,action){
    return{
        //状态:reducer(state.状态,action),
        todos:todos(state.todos,action),
        visibility:visibility(state.todos,action)
    }
}

4、创建store,并使用

store提供了三个API接口,分别是

  • getState:用于获取状态
  • dispatch:用于派发action
  • subscribe:当状态发生变化时执行的回调函数
//可在创建store时,也可在定义reducer时,初始化数据
const store = Redux.createStore(reducer,state)

使用回调函数,以及执行函数:

store.subscribe(() => {
    console.log(`现在的状态为:${store.getState().count}`)
})
store.dispatch(increment(1))
store.dispatch(increment(3))
store.dispatch(decrment())

react-redux

  • react-redux是链接react与redux的专用库。在使用时需要格外安装。学习react-redux最主要的就是学习其中的三个一,一种开发思想,一个connect方法,以及一个provide组件。
  • 1、一种开发思想:组件分离思想

react-redux提出了“智能”组件和“笨拙”组件相分离的开发思想,

  • “智能”组件,即容器组件,主要特点有:(1)负责管理数据和业务逻辑,不负责UI的呈现,(2)带有内部状态,(3)使用redux的API
  • “笨拙”组件,又称展示组件,特点:(1)只负责UI的呈现, 不带有任何的业务逻辑,(2)没有状态,(3)所有数据由参数(this.props)提供,(4)不适用redux的API。
  • 2、一个connect方法:将“笨拙”组件,变成“智能”组件

react-redux提供了一个connect方法,用于从“笨拙”组件生成容器组件。 其中状态,是指state数据,逻辑,是指修改state的方法.

  • 3、一个provider组件:为后代组件提供store对象。 在使用connect方法时,用到了store对象的dispatch方法,那么此时我们就需要使用provide组件,将store传递过去了。

还是以counter计数器为例,但是这次使用脚手架create-react-app的方式:

//安装脚手架create-react-app:
npm i create-react-app -g
//创建项目
create-react-app counter
//安装react-redux
npm i redux react-redux
//然后启动项目
npm start

counter案例:

1、先定义组件

//定义Show组件
export default class Show extends React.Component{
    render() {
      return (<h2>counter:{this.props.counter}</h2>)
    }
}
//定义Add组件,Sub组件与Add组件一样
export default class Add extends Component{
    render() {
      return (<button>+</button>)
    }
}
//定义counter组件,显示内容
class Counter extends Component {
    render() {
        return (
            <div>
                <Show/>
                <p><Add/>&nbsp;<Sub/>&nbsp;</p>
            </div>
        )
    }
}

2、创建store

  • 1、定义store以及action creators
//定义store
const state = {counter:6,}
//定义increment的action,定义decrement的action与其类似。
export function increment() {
    return {type: 'INCREMENT'}
}
  • 2、定义reducer以及合并
export default function counter(state = 0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DECREMENT':
            return state - 1
        default:
            return state
    }
}
//合并reducer
const reducer = combineReducers({
    counter// 当名字相同时,可以省略
})
  • 3、创建store
//可以在创建store时初始化数据
const store = createStore(reducer,state,)
//在使用框架,在不同的文件夹下写模块时,要记得导出以及导入你需要的数据。
export default store
  • 4、使用connect转化组件 在使用connect时,需要先导入必要的方法
import { connect } from 'react-redux'
//导入actions内的所有方法,并命名为action
import * as actions from '../actions/films'
import { bindActionCreators } from 'redux';

在导入必要的方法后,才能使用。

// 定义mapStarteTopProps,建立从state到props对象的映射,接受state作为参数
function mapStarteTopProps(state) {
    return {counter: state.counter}}
// 定义mapDispatchToProps,用于建立笨拙组件的参数到store.dispatch方法的映射,以dispatch为参数
function mapDispatchToProps(dispatch) {
    return bindActionCreators(action, dispatch)}
// 将Counter变成容器组件并导出,需要在顶层使用 provide组件,引入store,这样在子组件内就可以使用store了。
export default connect(mapStarteTopProps, mapDispatchToProps)(Counter)
  • 5、使用provider组件,传递store
//需要导入必要的包
import { Provider } from 'react-redux';
import store from './store'
import Counter from './containers/Counter';
//再使用provider方法
ReactDOM.render(
    // 将store集成到react应用的顶层props里面,然后使用provider组件传递。这样各个子组件就能访问到顶层的store对象了
    <Provider store={store}><Counter /></Provider>, 
    document.getElementById('root'));
registerServiceWorker();
  • 6、在各个子组件使用方法和属性

部分修改如下:

//在Add组件内使用increment方法
export default class Add extends Component{
    render() {
      return (<button onClick={()=>this.props.increment()}>+</button>)
    }
}
//修改Counter组件,显示数据,以及给子组件传递方法
class Counter extends Component {
    render() {
        return (
            <div>
                <Show counter={this.props.counter} />
                <p><Add increment={this.props.increment} />&nbsp;
                <Sub decrement={this.props.decrement} />&nbsp;
                </p>
            </div>
        )
    }
}
  • 测试如下:

在react-redux中实现异步操作

  • 定义异步的action creator函数

在异步action中,返回一个函数,在该函数中去调用同步的action即可。 这个函数,会自动的获取dispatch和getState。

//定义异步操作,在点击后两秒实现加操作
export function incrementAsync() {
    return function (dispatch, getState) {
        setTimeout(function () {
            dispatch(increment());
        }, 2000)
    }
}
  • 先前已经定义好了框架,现在只需在定义好的框架内,定义方法以及调用方法即可。
//在Counter内传递方法
<AsyncAdd incrementAsync={this.props.incrementAsync}
//在Async组件内使用方法
export default class AsyncAdd extends Component {
    render() {
        return (<button onClick={() => this.props.incrementAsync()}>+async</button>)
    }
}
  • redux默认是不支持异步action的,因此我们需要使用中间件来强化store,让Reducer 在异步操作结束后自动执行。
//安装实现异步操作的中间件redux-thunk
npm i redux-thunk -S
  • 使用中间件:
//先从redux-thunk内导入需要的方法
import thunkMiddleware from "redux-thunk"
// 在createStore方法上使用使用thunkMiddleware方法强化store
const  createStoreWithMiddleware = applyMiddleware(
    thunkMiddleware 
)(createStore)
const store = createStoreWithMiddleware(reducer,state,)

这样就可以在redux内实现异步操作了。