React 生态之 Redux

react_and_redux

Redux 初体验

通常会将 Redux 和 React 联系到一起,但是 Redux 不是寄生于 React 上,Redux 可以单独使用,也可以与 React 以外的第三方框架搭配使用。当然Redux 和 React 还是最好的搭配。Redux = Reducer + Flux ,这是 Redux 名字由来,所以不难看出 Redux 还是源于 Flux 而来,Flux 是为了 React 项目状态管理开发出来的。Redux 子所以备受欢迎,因为站在 Flux 肩膀上还回避掉了 Flux 的一些缺点。

现在 Redux 的作者 Dan 都被 Facebook 收了加入了 React 团队。所以 Redux 还是 React 对于状态说要选择。

今天我们可以脱离 React 先学习,学习 Redux
首先创建一个 React 项目,这就是运行 Redux 没有其他,你可以根据需要创建 vue 框架或者感觉 npm init -y 项目引入 rudex 然后 live-server 启动项目也是 OK,不过为了避免麻烦搭建脚手架,而且随后我们还是要回归到 React 上来分享 react-redux。

还是先分享一下 Redux 机制吧。

  • 创建一个 Store ,在 store 里面有 state 状态也就是数据,store 有两个接口(方法) dispatch 和 subscribe ,分别是订阅和发布事件。因为 Redux 就是一个事件订阅和发布的系统。
  • 在 Reducer ,这是一个纯函数,到底有多纯呢?就是接受 state(状态) 和 action(动作),接受两个参数就说明他还不是那么纯,应该是一个参数。reducer 负责根据 action 对 state 进行修改返回 state。
  • dispatch 用于发送 action
  • sucribe 用于订阅 state 的改变后进行处理。


    react-redux 机制

创建 store

引入 createStore 用于创建 store。

import { createStore } from 'redux';

定义 types 负责输出类型,所有事件(action)的类型都在这里定义,为了就是同样,为了避免不必要麻烦,也就是避免为人的录入错误。

export const FETCH_POSTS = 'FETCH_POSTS';
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const INCREMENT_ASYNC = 'INCREMENT_ASYNC'

导入类型文件,这里我们主要是简单的通过一个计数器来查看

创建 Reducer

import { INCREMENT, DECREMENT } from '../actions/types'

创建 reducer ,reducer 就是一个函数,reducer 接收两个参数 state 和 action,通过 action 更新 state 然后返回一个 state。

function counter(state = 0, action){
    switch(action.type){
        case INCREMENT:
            return state + 1
        case DECREMENT:
            return state - 1
        default:
            return state;
    }
}
  • 这里 state 初始值给 0,也就是计数器的初始值为 0。

通过 createStore 方法来创建一个 store。

let store = createStore(counter);

订阅事件,store 的 getState 方法可以过去状态 state。

store.subscribe(()=> console.log(store.getState()))

派发事件

store.dispatch({type:INCREMENT})

store.dispatch({type:INCREMENT});

store.dispatch({type:DECREMENT});
let store = createStore(counter);

完整代码

import React, { Component } from 'react';
import { createStore } from 'redux';

import { INCREMENT, DECREMENT } from '../actions/types'

function counter(state = 0, action){
    switch(action.type){
        case INCREMENT:
            return state + 1
        case DECREMENT:
            return state - 1
        default:
            return state;
    }
}

let store = createStore(counter);

store.subscribe(()=> console.log(store.getState()))

store.dispatch({type:INCREMENT})

store.dispatch({type:INCREMENT});

store.dispatch({type:DECREMENT});


window.store = store;

export default class ReduxPage extends Component{
    render(){
        return(
            <div>
                <h1>Redux Demo</h1>
            </div>
        )
    }
}

我们先看一看为什么 React 需要 Redux 呢?

export default class index extends Component {
    constructor(){
        super();
        this.state = {
            tut:"angularjs"
        }
    }

    updateTut(newTut){
        this.setState({
            tut:newTut
        });
    }
    render() {
        return (
            <div>
                <Main changeTut={this.updateTut.bind(this)}/>
                <Tut tut={this.state.tut}/>
            </div>
        )
    }
}

上面代码 state 散落到 React 应用的各个角落不利于管理。Tut 接受 this.state.tut 然后 Main 由更新了 state

 const Main = (props) => {
        return (
            <div>
                <button
                    onClick={()=>props.changeTut('angular')}>
                        update angular
                    </button>
            </div>
        )
}

这样一来缺乏统一管理,对于小型应用我们可以凭借良好记忆,但是对于大型应用,这样势必是难于维护和扩展的。

其实从上面代码来看,redux 并没有那么复杂,就是一个对订阅和发布设计模式实现的框架。当然没有那么简单,但是无论他有多复杂其本质就是订阅和发布模式。

现在我们开始整合到 react 中,首先不使用 react-redux 直接。

先定义事件的类型

export const SET_NAME = "SET_NAME";
export const  SET_TUT = "SET_TUT";

export const ADD = "ADD";
export const SUBTRACT = "SUBTRACT";

定义事件类型,通常是全部大写,单词用 _ 进行分割,以动词开头动词加名称的结构。

定义 Actions

import {SET_NAME,SET_TUT} from "../reducers/types";
 export function setName(name){
     return{
         type:SET_NAME,
         payload:name
     }
 }

 export function setTut(tut){
    return{
        type:SET_TUT,
        payload:tut
    }
}

定义 Reducers

其实 reducer 也可以理解为一个规则定义器。制定 action 是如何更新 state 的规则。

import {SET_NAME,SET_TUT} from "./types"
const userReducer = (state = {
    name:"zidea",
    tuts:50
  }, action) => {
    switch (action.type) {
        case SET_NAME:
            state = {
                ...state,
                name:action.payload
            }
            break;
  
        case SET_TUT:
            state = {
                ...state,
                tuts:action.payload
            }
  
            break;
    }
  
    return state;
  }

  export default userReducer

创建 store 对象

import { createStore,combineReducers,applyMiddleware } from 'redux';

import logger from 'redux-logger'

import math from '../reducers/mathReducer';
import user from '../reducers/userReducer'

import thunk from 'redux-thunk';
import promise from 'redux-promise';

const mLogger = (store) => (next) => (action) => {
    console.log("Logged Action: ", action);
    next(action)
 }
 
 const store = createStore(combineReducers(
     {
         math,
         user
        }),
   {},
   applyMiddleware(mLogger,logger,thunk,promise));


   export default store;

  • 这里顺便说一下中间件
    中间件事位于 action 和 reducer 之间,在传递 action 同时进行一些操作,如果 next(action) 向下传递 action 动作将不会被执行。

将 store 与应用关联

import AppWithRedux from './AppWithRedux'

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

function App() {
  return (
    <Provider store={store}>

    <div className="App">
      {/* <AdvApp/> */}

      <AppWithRedux/>
    </div>
    </Provider>
  );
}

export default App;

-通过 react-redux 提供 Provider 可以将 store 共享到整个应用中去。

重写组件,组件与 Redux 关联

import React, { Component } from 'react'
import Tut from '../AppWithoutRedux/components/Tut';
import Main from '../AppWithoutRedux/components/Main';


import {Provider,connect} from 'react-redux'
import {setName} from "./actions/userActions";


class index extends Component {
    constructor(){
        super();
       
    }

    updateTut(newTut){

    }
    
    render() {
        return (
            
            <div>
                <h1>React 和 Redux</h1>
                <Main changeTut={() => this.props.setName('tina')}/>
                <Tut tut={this.props.user.name}/>
            </div>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        user:state.user,
        math:state.math
    }
}

const mapDispatchToProps = (dispatch) => {
    return{
        setName:(name)=>{
            dispatch(setName(name))
        }
    }
}

export default connect(mapStateToProps,mapDispatchToProps)(index)

从 react-redux 引入 connect 负责将组件和 redux 相关联。关联接受两个 mapStateToProps 和 mapDispatchToProps。当然可以根据自己喜好起其他名称,

  • mapStateToProps: 我们知道每一个 reducer 都有一个 state ,mapStateToProps 负责将 reducer 中 state 某个属性对应到 props 上属性。
  • mapDispatchToProps 传入 dispatch 返回一个以对象这个对象里方法会添加 props 从而达到将 props 发放可以发送更新 state 的事件。

现在问题是 store 的订阅是在哪里做的呢?也就是 subscribe 方法怎么实现的。

无需 redux-react 实现 redux 管理组件状态

import {Provider,connect} from 'react-redux'
constructor(props){
    super(props);
    this.state = { counter: store.getState()}
    
    store.subscribe(()=> this.setState({
        counter:store.getState()
    }))
    this.increment = this.increment.bind(this);
    this.decrement = this.decrement.bind(this);
}
  • 将 store 的 state 和组件的 state 进行关联
  • 在构造函数中订阅 store 的事件,根据事件更新组件 state
increment(){
    store.dispatch({type:INCREMENT});
}

decrement(){
    store.dispatch({type:DECREMENT});
}
  • 在动作中发布事件,这样大家就清晰 redux-react 的实现
export default class ReduxPage extends Component{
    constructor(props){
        super(props);
        this.state = { counter: store.getState()}
        
        store.subscribe(()=> this.setState({
            counter:store.getState()
        }))
        this.increment = this.increment.bind(this);
        this.decrement = this.decrement.bind(this);
    }

    increment(){
        store.dispatch({type:INCREMENT});
    }

    decrement(){
        store.dispatch({type:DECREMENT});
    }
    render(){
        return(
            <div>
                <h1>Redux Demo</h1>
                <p>{this.state.counter}</p>
                <div>
                    <button 
                        onClick={this.increment}
                        >increment</button>
                    <button
                        onClick={this.decrement}
                        >decrement</button>
                </div>
            </div>
        )
    }
}

我们将 redux 从项目抽离出来,作为组件单独来使用,store 作为组件 props 属性传入到组件

return(
    <div>
        <h1>Redux Demo</h1>
        <Counter store={store}/>
    </div>
)

定义组件,在组件构造函数里处理订阅事件。

constructor(props){
    super(props);
    console.log(props)
    this.state = {
        counter: props.store.getState()
    }

    props.store.subscribe(()=> this.setState({
        counter:props.store.getState()
    }))
    this.increment = this.increment.bind(this);
    this.decrement = this.decrement.bind(this);
}

应用端完整代码

import React, { Component } from 'react';
import { createStore } from 'redux';

import { INCREMENT, DECREMENT } from '../actions/types'
import Counter from '../components/Counter';

function counter(state = 0, action){
    switch(action.type){
        case INCREMENT:
            return state + 1
        case DECREMENT:
            return state - 1
        default:
            return state;
    }
}

let store = createStore(counter);

export default class ReduxPage extends Component{
    constructor(props){
        super(props);
    }

    render(){
        return(
            <div>
                <h1>Redux Demo</h1>
                <Counter store={store}/>
            </div>
        )
    }
}

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { INCREMENT, DECREMENT } from '../actions/types'

class Counter extends Component {

    constructor(props){
        super(props);
        console.log(props)
        this.state = {
            counter: props.store.getState()
        }

        props.store.subscribe(()=> this.setState({
            counter:props.store.getState()
        }))
        this.increment = this.increment.bind(this);
        this.decrement = this.decrement.bind(this);
    }

    increment(){
        this.props.store.dispatch({type:INCREMENT});
    }

    decrement(){
        this.props.store.dispatch({type:DECREMENT});
    }

    render(){
        return(
            <div>
                <p>{this.state.counter}</p>
                <div>
                    <button 
                        onClick={this.increment}
                        >increment</button>
                    <button
                        onClick={this.decrement}
                        >decrement</button>
                </div>
            </div>
        )
    }
}


export default Counter;

希望我的分享对大家有所帮助。

react 生态
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容