【redux】redux和react-redux的学习

1.什么场景使用redux?

1.项目角度

  • 用户的使用方式复杂 
  • 不同身份的用户有不同的使用方式(多角色)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了websocket
  • view要从多个来源获取数据

2.组件角度

  • 某个组件的状态需要共享 
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态

 

2.redux设计思想

  • web应用是一个状态机,视图与状态一一对应的 
  • 所有的状态,保存在一个对象里面

 

3.redux三大原则

  • 唯一数据源
  • 保持只读状态
  • 数据改变只能通过纯函数来执行

 

4.redux流程图示意

 

 

5.redux版TodoList(示例原始的redux)

页面示例:

 

 

1.抽离默认数据到reducer(定义reducer)

//reducer.js

//1.定义默认的状态数据
const defaultState = {
    inputVal: '',
    data: [
        '尝遍人间甘辛味,言外冷暖我自知',
        '一切安乐,无不来自困苦',
        '如果脏了还用,就莫不如一开始就用带颜色的。白的就要纯白才行',
        '君といると、月が绮丽です',
        '得来不易的机会,会让所有的动物去做原来不喜欢做的事',
    ]
}


// 2.导出一个返回状态数据的函数
// 该函数有两个参数:state/action
// 将defaultState赋值给state,使得state有初始化数据,为后面使用state埋下伏笔
export default (state = defaultState,action) => {
    return state;
}

 

2.使页面上使用state中的数据(即获取state的数据)

 创建store以使用

//store/index.js

import { createStore } from 'redux';
import reducer from './reducer';

// 使用createStore 来引用reducer中返回的数据
const store = createStore(reducer);
export default store;

 

 

通过store.getState() 赋值给react state,以使state获得数据并显示在页面上。

如果这时想要更改state中的数据呢?

//TodoList.js

import React,{ Component } from 'react';
import { Button,Input,List } from 'antd';
// 引入store
import store from './store';

export default class TodoList extends Component{
    constructor(){
        super();
        // getState获取store中的状态
        this.state = store.getState();
    }
    
    render(){
        return (
            <div style={{padding: '40px'}}>
                <Input 
                placeholder="请输入待办事项" 
                style={{ width: '350px',marginRight: '20px' }}
                value={this.state.inputVal}
                />
                <Button 
                type="primary" 
                >
                    添加
                </Button>
                <List
                bordered
                style={{ marginTop: '30px',width: '435px',userSelect: 'none' }}
                dataSource={this.state.data}
                renderItem={(item,index) => (
                    <List.Item>{item}</List.Item>
                )}
                />
            </div>
        )
    }
}

 

3.通过定义action对象变更state中的数据(dispatch:事件派发)

 定义action对象以便派发事件

 通过store.dispatch(action对象)派发事件

//TodoList.js 

import React,{ Component } from 'react';
import { Button,Input,List } from 'antd';
// 引入store
import store from './store';


export default class TodoList extends Component{
    constructor(){
        super();
        // getState获取store中的状态
        this.state = store.getState();
    }
   
    // 输入框变化
    handleChange(e){
        // 触发reducer(store)修改数据
        const action = {
            // type必传
            type: 'change_inputVal',
            value: e.target.value
        };
        //dispatch 事件派发
        store.dispatch(action);
    }
    
    // 新增
    handleClick(){
        const action = {
            type: 'add_to_data'
        }
        store.dispatch(action);
    }
    
    //双击删除
    handleDoubleClick(index){
        store.dispatch({
            type: 'del_to_data',
            value: index
        })
    }

    render(){
        return (
            <div style={{padding: '40px'}}>
                <Input 
                placeholder="请输入待办事项" 
                style={{ width: '350px',marginRight: '20px' }}
                value={this.state.inputVal}
                onChange={this.handleChange.bind(this)} 
                />
                <Button 
                type="primary" 
                onClick={this.handleClick.bind(this)}>
                    添加
                </Button>
                <List
                bordered
                style={{ marginTop: '30px',width: '435px',userSelect: 'none' }}
                dataSource={this.state.data}
                renderItem={(item,index) => (
                    <List.Item onDoubleClick={this.handleDoubleClick.bind(this,index)}>{item}</List.Item>
                )}
                />
            </div>
        )
    }
}

 将reducer中的导出状态函数补充完整,修改state的具体逻辑在该函数中

 三点需要注意:

  • let newState = JSON.parse(JSON.stringify(state)) 可谓是该函数中最重要的一句代码,直接将reducer与state撇清关系;自此后续所有操作都围绕newState进行,可以说,初始化的defaultState的唯一作用只是为newState提供了数据的原始版本
  • 该函数最后返回的是newState,而不是state,newState代表的永远是最新的数据
  • action的作用只是通过属性type值区分逻辑操作而已,推荐使用switch...case语句
//store/reducer.js

//1.定义默认的状态数据
const defaultState = {
    inputVal: '',
    data: [
        '尝遍人间甘辛味,言外冷暖我自知',
        '一切安乐,无不来自困苦',
        '如果脏了还用,就莫不如一开始就用带颜色的。白的就要纯白才行',
        '君といると、月が绮丽です',
        '得来不易的机会,会让所有的动物去做原来不喜欢做的事',
    ]
}


// 2.导出一个返回状态数据的函数
// 该函数有两个参数:state/action
export default (state = defaultState,action) => {
    // reducer 只能接收和返回新的state,不能直接更改state,所以需要对state进行深拷贝
    // 到这里其实 newState与state(最开始默认的defaultState)并无关系,
    // 往后所有操作都是操作newState,显然,此时defaultState只是一个摆设
    let newState = JSON.parse(JSON.stringify(state));

    //reducer接收到一个事件,先判断不同的类型
    switch (action.type) {
        case 'change_inputVal':
            newState.inputVal = action.value 
            break;
        case 'add_to_data':
            newState.data.push(newState.inputVal);
            newState.inputVal = '';
            break;
        case 'del_to_data':
            newState.data.splice(action.value,1);
            break;    
        default:
            break;
    }
    // 始终返回的都是深拷贝的newState
    return newState;
}


 

4.store数据的订阅(subscribe:数据订阅)

 subscribe(函数),该函数在store变化时,去更改react state中的数据

//TodoList.js 

import React,{ Component } from 'react';
import { Button,Input,List } from 'antd';
// 引入store
import store from './store';


export default class TodoList extends Component{
    constructor(){
        super();
        // getState获取store中的状态
        this.state = store.getState();
        // subscribe 订阅
        // 订阅store ,一旦store 状态数据有更新,会进行触发
        store.subscribe(this.storeChange.bind(this));

    }
    
    // store变化时,修改react state的函数
    storeChange(){
        this.setState(store.getState());
    }
    
    // 输入框变化
    handleChange(e){
        // this.setState({
        //     inputVal: e.target.value
        // });
        // 触发reducer(store)修改数据
        const action = {
            // type必传
            type: 'change_inputVal',
            value: e.target.value
        };
        //dispatch 事件派发
        store.dispatch(action);
    }
    
    // 新增
    handleClick(){
        const action = {
            type: 'add_to_data'
        }
        store.dispatch(action);
    }
    
    //双击删除
    handleDoubleClick(index){
        store.dispatch({
            type: 'del_to_data',
            value: index
        })
    }

    render(){
        return (
            <div style={{padding: '40px'}}>
                <Input 
                placeholder="请输入待办事项" 
                style={{ width: '350px',marginRight: '20px' }}
                value={this.state.inputVal}
                onChange={this.handleChange.bind(this)} 
                />
                <Button 
                type="primary" 
                onClick={this.handleClick.bind(this)}>
                    添加
                </Button>
                <List
                bordered
                style={{ marginTop: '30px',width: '435px',userSelect: 'none' }}
                dataSource={this.state.data}
                renderItem={(item,index) => (
                    <List.Item onDoubleClick={this.handleDoubleClick.bind(this,index)}>{item}</List.Item>
                )}
                />
            </div>
        )
    }
}

 到这里redux版TodoList已经完成

 

 

6.actionType(封装action对象的type常量)  

以上述redux版TodoList为例: 

 在没有封装actionCreaters的情况,actionType封装点有两处:

  • reducer中switch...case中的action属性type
  • 页面代码(TodoList)中的定义的action对象

 新建action type常量文件 

//store/actionTypes.js

//定义action type 常量值
export const CHANGE_INPUTVALUE = 'change_inputVal';
export const ADD_TO_DATA = 'add_to_data';
export const DEL_TO_DATA = 'del_to_data';

改造两处需封装的地方:

//store/reducer.js

import { CHANGE_INPUTVALUE,ADD_TO_DATA,DEL_TO_DATA } from './actionType';

//1.定义默认的状态数据
const defaultState = {
    inputVal: '',
    data: [
        '尝遍人间甘辛味,言外冷暖我自知',
        '一切安乐,无不来自困苦',
        '如果脏了还用,就莫不如一开始就用带颜色的。白的就要纯白才行',
        '君といると、月が绮丽です',
        '得来不易的机会,会让所有的动物去做原来不喜欢做的事',
    ]
}


// 2.导出一个返回状态数据的函数
// 该函数有两个参数:state/action
export default (state = defaultState,action) => {
    // reducer 只能接收和返回新的state,不能直接更改state,所以需要对state进行深拷贝
    // 到这里其实 newState与state(最开始默认的defaultState)并无关系,
    // 往后所有操作都是操作newState,显然,此时defaultState只是一个摆设
    let newState = JSON.parse(JSON.stringify(state));

    //reducer接收到一个事件,先判断不同的类型
    switch (action.type) {
        case CHANGE_INPUTVALUE:
            newState.inputVal = action.value 
            break;
        case ADD_TO_DATA:
            newState.data.push(newState.inputVal);
            newState.inputVal = '';
            break;
        case DEL_TO_DATA:
            newState.data.splice(action.value,1);
            break;    
        default:
            break;
    }
    // 始终返回的都是深拷贝的newState
    return newState;
}


//TodoList.js 

import React,{ Component } from 'react';
import { Button,Input,List } from 'antd';
// 引入store
import store from './store';

//引入action type
import { CHANGE_INPUTVALUE,ADD_TO_DATA,DEL_TO_DATA } from './actionType';


export default class TodoList extends Component{
    constructor(){
        super();
        // getState获取store中的状态
        this.state = store.getState();
        // subscribe 订阅
        // 订阅store ,一旦store 状态数据有更新,会进行触发
        store.subscribe(this.storeChange.bind(this));

    }
    
    // store变化时,修改react state的函数
    storeChange(){
        this.setState(store.getState());
    }
    
    // 输入框变化
    handleChange(e){
        const action = {
            // type必传
            type: CHANGE_INPUTVALUE,
            value: e.target.value
        };
        //dispatch 事件派发
        store.dispatch(action);
    }
    
    // 新增
    handleClick(){
        const action = {
            type: ADD_TO_DATA
        }
        store.dispatch(action);
    }
    
    //双击删除
    handleDoubleClick(index){
        store.dispatch({
            type: DEL_TO_DATA,
            value: index
        })
    }

    render(){
        return (
            <div style={{padding: '40px'}}>
                <Input 
                placeholder="请输入待办事项" 
                style={{ width: '350px',marginRight: '20px' }}
                value={this.state.inputVal}
                onChange={this.handleChange.bind(this)} 
                />
                <Button 
                type="primary" 
                onClick={this.handleClick.bind(this)}>
                    添加
                </Button>
                <List
                bordered
                style={{ marginTop: '30px',width: '435px',userSelect: 'none' }}
                dataSource={this.state.data}
                renderItem={(item,index) => (
                    <List.Item onDoubleClick={this.handleDoubleClick.bind(this,index)}>{item}</List.Item>
                )}
                />
            </div>
        )
    }
}

 

7.actionCreaters(封装action对象)

在以上示例的基础上封装action对象,只有一处封装点: 

  •  将页面代码(TodoList)中定义的对象进行封装
  • 由于action单独抽离,actionType也会随action的封装而去
//store/actionCreaters.js


//引入action type
import { CHANGE_INPUTVALUE,ADD_TO_DATA,DEL_TO_DATA } from './actionType';

// 定义action 对象

// 输入框变化action
export const CHANGE_INPUTVALUE_ACTION = (value) => {
    return {
        type: CHANGE_INPUTVALUE,
        value
    }
}


// 添加事项action
export const ADD_TO_DATA_ACTION = () => {
    return {
        type: ADD_TO_DATA
    }
}


// 删除事项action
export const DEL_TO_DATA_ACTION = (value) => {
    return {
        type: DEL_TO_DATA,
        value
    }
}


//TodoList.js 

import React,{ Component } from 'react';
import { Button,Input,List } from 'antd';
// 引入store
import store from './store';

//引入actionCreaters
import { 
    CHANGE_INPUTVALUE_ACTION,
    ADD_TO_DATA_ACTION,
    DEL_TO_DATA_ACTION
} from './store-react-redux/actionCreaters';


export default class TodoList extends Component{
    constructor(){
        super();
        // getState获取store中的状态
        this.state = store.getState();
        // subscribe 订阅
        // 订阅store ,一旦store 状态数据有更新,会进行触发
        store.subscribe(this.storeChange.bind(this));

    }
    
    // store变化时,修改react state的函数
    storeChange(){
        this.setState(store.getState());
    }
    
    // 输入框变化
    handleChange(e){
        //dispatch 事件派发
        store.dispatch(CHANGE_INPUTVALUE_ACTION(e.target.value));
    }
    
    // 新增
    handleClick(){
        const action = {
            type: ADD_TO_DATA
        }
        store.dispatch(ADD_TO_DATA_ACTION());
    }
    
    //双击删除
    handleDoubleClick(index){
        store.dispatch(DEL_TO_DATA_ACTION(index));
    }

    render(){
        return (
            <div style={{padding: '40px'}}>
                <Input 
                placeholder="请输入待办事项" 
                style={{ width: '350px',marginRight: '20px' }}
                value={this.state.inputVal}
                onChange={this.handleChange.bind(this)} 
                />
                <Button 
                type="primary" 
                onClick={this.handleClick.bind(this)}>
                    添加
                </Button>
                <List
                bordered
                style={{ marginTop: '30px',width: '435px',userSelect: 'none' }}
                dataSource={this.state.data}
                renderItem={(item,index) => (
                    <List.Item onDoubleClick={this.handleDoubleClick.bind(this,index)}>{item}</List.Item>
                )}
                />
            </div>
        )
    }
}

以上两中封装,说好听点呢,就是方便管理使用分类协作,说难听点呢,就是搞形式主义。。。

 

 

8.在redux的基础上整合react-redux

react-redux本身依赖于redux,安装前必须安装redux 

npm install react-redux

 整合react-redux,下述主要包括三部分:

  • Provider提供器组件,用于在各个组件间传递store数据
  • connect连接器函数用于映射state和action在props上(建立react与state/props的关系)

 

1.Provider提供器

 Provider是一个提供器,只要使用了这个组件,组件里的其他所有组件都可以使用store了,这是react-redux的核心组件

//src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import Todolist from './TodoList'; //介绍state,派发,订阅
import Leijia from './Leijia';//介绍action类型常量,actionCreater
import TodoListRR from './TodoListRR';//将之前的Todolist改造成封装了action类型常量/actionCreater,并整合react-redux

// 加入 react-redux
import store from './store-react-redux';
// 引入提供器组件
import { Provider } from 'react-redux';
// 放在Provider中的标签组件都可以使用Provider提供的数据

ReactDOM.render(
  // <Todolist />,
  // <Leijia />,
  // <TodoListRR />,
  <Provider store={store}>
    <TodoListRR /> 
  </Provider>,
  document.getElementById('root')
);

 

2.connect连接器 

//TodoList.js

import React,{ Component } from 'react';
import { Button,Input,List } from 'antd';
// 引入store
//import store from './store';

//引入actionCreaters
import { 
    CHANGE_INPUTVALUE_ACTION,
    ADD_TO_DATA_ACTION,
    DEL_TO_DATA_ACTION
} from './store-react-redux/actionCreaters';


// 引入连接器函数,这时候会不需要store,将原先的store代码删除
import { connect } from 'react-redux';


class TodoListRR extends Component{
    
    render(){
        return (
            <div style={{padding: '40px'}}>
                <Input 
                placeholder="请输入待办事项" 
                style={{ width: '350px',marginRight: '20px' }}
                value={this.props.inputVal}
                onChange={this.props.handleChange.bind(this)} 
                />
                <Button 
                type="primary" 
                onClick={this.props.handleClick.bind(this)}>
                    添加
                </Button>
                <List
                bordered
                style={{ marginTop: '30px',width: '435px',userSelect: 'none' }}
                dataSource={this.props.data}
                renderItem={(item,index) => (
                    <List.Item onDoubleClick={this.props.handleDoubleClick.bind(this,index)}>{item}</List.Item>
                )}
                />
            </div>
        )
    }
}

//引入连接器后,不再是导出组件,而是导出connect(状态的映射,事件派发的映射)(组件名称)
export default connect()(TodoListRR);

 

3.state与action的映射

 这两者的映射主要是建议与react 组件 props的关系,使得具体业务逻辑可以直接使用props上映射的state和action 

import React,{ Component } from 'react';
import { Button,Input,List } from 'antd';
// 引入store
//import store from './store';

//引入actionCreaters
import { 
    CHANGE_INPUTVALUE_ACTION,
    ADD_TO_DATA_ACTION,
    DEL_TO_DATA_ACTION
} from './store-react-redux/actionCreaters';


// 引入连接器函数,这时候会不需要store,将原先的store代码删除
import { connect } from 'react-redux';


class TodoListRR extends Component{
    render(){
        return (
            <div style={{padding: '40px'}}>
                <Input 
                placeholder="请输入待办事项" 
                style={{ width: '350px',marginRight: '20px' }}
                value={this.props.inputVal}
                onChange={this.props.handleChange.bind(this)} 
                />
                <Button 
                type="primary" 
                onClick={this.props.handleClick.bind(this)}>
                    添加
                </Button>
                <List
                bordered
                style={{ marginTop: '30px',width: '435px',userSelect: 'none' }}
                dataSource={this.props.data}
                renderItem={(item,index) => (
                    <List.Item onDoubleClick={this.props.handleDoubleClick.bind(this,index)}>{item}</List.Item>
                )}
                />
            </div>
        )
    }
}
//状态映射
const mapStateToProps = (state) => {
    return {
        ...state
    }
}

// 事件派发映射
const mapDispatchToProps = (dispatch) => {
    return {
        // 新增
        handleClick(){
            dispatch(ADD_TO_DATA_ACTION());
        },
        //双击删除
        handleDoubleClick(index){
            dispatch(DEL_TO_DATA_ACTION(index));
        },
        handleChange(e){
            dispatch(CHANGE_INPUTVALUE_ACTION(e.target.value));
        }
    }
}


//引入连接器后,不再是导出组件,而是导出connect(状态的映射,事件派发的映射)(组件名称)
export default connect(mapStateToProps,mapDispatchToProps)(TodoListRR);

 tips:每一个组件内都这样写吗?过于麻烦

 

后续有时间在关注redux和react-redux在企业项目上的拆分

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值