看得明白的react-redux教程

前言

redux核心很简单。

redux

redux核心

redux 很简单,总结起来就是一句话:一个带事件通知的状态保存者。
看代码,如里下面代码看明白了,直接跳至 插件

const {createStore} = require('redux');

function reducer(state, action) {
    switch (action.type) {
        case 'add':
            return {value: state.value + action.payload}
        case 'dec':
            return {value: state.value - action.payload}
        default:
            return state
    }
}

const store = createStore(reducer, {value: 1})
const subscriber = () => {
    console.log("状态变化了", store.getState())
};
store.subscribe(subscriber)
store.dispatch({type: 'add', payload: 2})
store.dispatch({type: 'dec', payload: 1})

运行结果

状态变化了 { value: 3 }
状态变化了 { value: 2 }

代码极其简单,但redux 的所有核心(全部)内容也就在这里了。只有一个createStore函数,用来创建一个 store 来保存状态,传入的参数是一到三个。第一个参数是叫reducer函数, 作用是产生状态,每二个参数是初始状态,如果有第三个参数,则是插件。只有第一个参数是必须的。

store对象

就三个方法

  1. getState() , 用来取状态
  2. dispatch(action),告诉store 一个事件,只要是个对象就行,对象格式无要求,只要reducer认识就行。大多数按官网写法,写成 {type:‘someKey’,payload:‘someValue’} ,代表事件名称,及参数
  3. subscriber(callback) , 当状态变化后,通知callback , 这个通知极其简陋,只告诉你变化了,但不告诉你哪里变化了。

reducer 函数

redux 把生成状态的函数叫reducer ,接受两个参数,第一个当前状态,第二个就是事件内容。
需要注意两点

  1. 无论如何要返回一个状态,比如下面的例子,缺少了default 的处理,就会出错,因为少了default处理,在初始化的时候状态变成undefined
function reducer(state, action) {
    switch (action.type) {
        case 'add':
            return {value: state.value + action.payload}
        case 'dec':
            return {value: state.value + action.payload}
        // default: 这个注释掉就出错
        //     return state
    }
}
  1. 返回的新状态必须与旧状态是不同的对象,因为redux 只是简单比较两个对象,象以下这么写,是触发不了状态变化的通知,因为返回了旧的state 对象
function reducer(state, action) {
   switch (action.type) {
       case 'add':
           state.value =  state.value + action.payload
           return state  // 返回了旧的state 对象 
      //.,..
   }
}

redux 工作过程

初始化

createStore 调用reducer ,传入一个参数 :初始状态,也就是createStore的第二个参数。调用后,得到初始状态,保存下来。

运行时

三步曲:

用dispatch 发一个事件 => store调用reducer=>发出通知 subscribe

插件

插件就是拦截 store.dispatch 方法,写法是固定的,下面以一个日志插件为例

function logger({dispatch, getState}) {
    //此dispatch 方法是未被拦截的dispatch
    return (next) => {
        // next 是指下一个插件
        return (action) => {
            //需要填写的内容在此
            console.log('before log', action, getState())
            next(action)
            console.log('after log', getState)
        }
    }
}

const store = createStore(reducer,applyMiddleware(logger))

插件可以形成一个串行调用链。上面例子中的 applyMiddleware 方法 由redux提供,参数是可以多个插件,作用就是将多个插件串起来。

redux 其它内容

redux 官网其它内容都是在讲编程规范与技巧

取消“魔法”字符串和对象

在上述例子中,写事件时,都是直接用字符串,比如"add",“dec”,如果多个地方用的话,会容易出错。编程中将这类容易出错的字符串叫“魔法”字符串。解决方法就是定义常量。

const TYPE_ADD="TYPE_ADD"
const TYPE_DEC="TYPE_DEC"

同理,产生事件也变成一个方法:

function createAddAction(value){
	return {type:TYPE_ADD,payload:value}
}

combineReducers 合并多个reducer

如果有多个状态内容,比如user 的状态,post 的状态,如果都写在一个文件里,会太庞大。所以,用combineReducers这个方法合并

function userReducer(state, action) {
   //省略
}
function postReducer(state, action) {
   //省略
}
const reducer = combineReducers({user: userReducer, post: postReducer})

这样产生出来的state 就象:

{
   user:{}
   post:{}
}

异步调用

确切的说 :redux 并不支持异步,因为reducer必须是同步的。store接收到dispatch过来的action后,直接调用reducer,得到结果,这个机制无法改变。所有的异步,都得发生在dispatch之前。所以,异步得自己管。
有几个插件,可以协助异步,原理都是拦截dispatch方法,根据action类型或者内容,执行相应操作。

  1. redux-thunk 让action 可以是函数
  2. redux-saga 监听action.type ,接管特定action ,触发一些异步动作
  3. redux-promise 让action 可以是Promise对象

监听结果

redux.subscribe只告诉状态变化了,但不知道哪个内容变化,用redux-observer可以监听指定内容。

react-redux 粘合剂

redux 与react 结合时,不用 react-redux 也可工作。状态改变后,用store.subscriber 通知react重渲染

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

function reducer(state, action) {
   switch (action.type) {
       case 'add':
           return {value: state.value + action.payload}
       case 'dec':
           return {value: state.value - action.payload}
       default:
           return state
   }
}

const store = createStore(reducer, {value: 1})
const subscriber = () => {
     ReactDOM.render(
        <div>
            <div>{store.getState().value}</div>
            <button onClick={() => store.dispatch({type: 'add', payload: 1})}>1</button>
            <button onClick={() => store.dispatch({type: 'dec', payload: 1})}>1</button>
        </div>,
        document.getElementById('root')!
    )
};
store.subscribe(subscriber)

这样写,主要的缺陷就是组件与store 耦合紧密,不利于调试与复用,所以就用到react-redux 来解耦与粘合。

Provider 与 connect

主要就两个内容:Provider 组件与 connect 方法。 Provider 用来提供全局的状态提供者,connect 将普通Component包裹一下,能够接收Provider提供的状态。

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from "redux";
import {Provider,connect} from 'react-redux'

function reducer(state, action) {
    switch (action.type) {
        case 'add':
            return {value: state.value + action.payload}
        case 'dec':
            return {value: state.value - action.payload}
        default:
            return state
    }
}

const store = createStore(reducer, {value: 1})

//以下开始不一样

class Counter extends Component{
   render(){
     return(
     <div>
       <div>{this.props.value}</div>
       <button onClick={this.props.addMethod}>1</button>
       <button onClick={this.props.decMethod}>1</button>
     </div>
    ) 
   }
}
function mapStateToProps(state){
	return {
	  value:state.value
	}
}
function mapDispatchToProps(dispatch){
    return {
      addMethod:()=>dispatch({type: 'add', payload: 1})
      decMethod:()=>dispatch({type: 'dec', payload: 1})
    }
}
const WrapedCounter=connect(mapStateToProps,mapDispatchToProps)(Counter)

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

connect 方法需要两个函数参数,一个是mapStateToProps ,把store中的状态映射到组件的属性。一个是mapDispatchToProps,把发送事件的方法映射成组件的属性。

总结

谢谢阅读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值