zoukankan      html  css  js  c++  java
  • React 之 Redux 的使用

    Redux可以简单概况为:将需要修改的state都存入到store里,发起一个action用来描述发生了什么,用reducers描述action如何改变state tree 。创建store的时候需要传入reducer,真正能改变store中数据的是store.dispatch API。

    1. Redux使用场景

    1. 某个组件的状态,需要共享
    2. 不同组件之间通信

    2. Redux 特点


    2.1 Store

    store是一个数据仓库,一个应用中store是唯一的,它里面封装了state状态,当用户想访问state的时候,只能通过store.getState()来取得state对象。

    2.2 action

    action描述了一个更新state的动作,它是一个对象,其中type属性是必须有的,它指定了某动作和要修改的值:

    {
        type: CHANGE_INPUT_VALUE,
        value: 'abc'
    }
    

    2.3 actionCreator

    actionCreator 是一个方法,用来创建action对象,调用这个方法就能返回一个action对象,用于简化代码

    2.4 dispatch

    dispatch 是一个方法,它用于派发一个动作action,这是唯一的一个能够修改state的方法,它内部会调用reducer来调用不同的逻辑基于旧的state来更新出一个新的state。

    2.5 reducer

    reducer是更新state的核心,它里面封装了更新state的逻辑,reducer由外界提供(封装业务逻辑,在createStore时传入),并传入旧state对象和action,将新值更新到旧的state对象上返回。

    3. Redux 三大原则

    3.1 单一数据源

    整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。组件通过如下方式取出state中数据:

    console.log(store.getState())
    

    3.2 State 是只读的

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

    store.dispatch({
      type: 'CHANGE_INPUT_VALUE',
      value: 'abc'
    })
    

    3.3 使用纯函数来执行修改 reducers

    为了描述 action 如何改变 state tree ,你需要编写 reducers。Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。

    const defaultState = { // 默认一个state
        inputValue: 'abc',
        list: [1,2,3]
    };
     
    export default (state = defaultState, action) => {
        console.log(state, action);
        if (action.type === 'CHANGE_INPUT_VALUE') {
            let newState = JSON.parse(JSON.stringify(state)); // 深拷贝一份state
            newState.inputValue = action.value;  // 更改拷贝后的state
            return newState;  // 将更新后的新的state 返回给 store
        } 
        return state;
    }
    

    4 示例应用 - Todolist


    入口文件 /src/index.js:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import Todolist from './Todolist';
    
    ReactDOM.render(<Todolist />, document.getElementById('root'));
    
    

    新建文件 src/Todolist.js

    import React, { Component, Fragment } from 'react'; 
    
    class TodoList extends Component {
     
      render() {
        return (
          <Fragment>
            <div style={{padding: '10px'}}> 
              todolist
            </div>
          </Fragment>
        )
      }
    }
    
    export default TodoList;
    
    

    4.1 创建 store

    新建目录: /src/store

    4.2 store 入口文件

    创建文件:/src/store/index.js:

    import { createStore } from 'redux'
    import reducer from './reducer'
    
    const store = createStore(reducer)
    
    export default store
    
    

    4.3 统一定义action.type

    新建文件:/src/store/actionType.js,

    export const CHANGE_INPUT_VALUE = 'change_input_value';  // 更改input处输入框里面的值
    export const ADD_TOTO_ITEM = 'add_todo_item';  // 添加待办事项
    export const DELETE_TODO_ITEM = 'detete_todo_item';   // 删除待办事项
    

    4.4 创建action对象

    新建文件:/src/store/actionCreator.js

    // 引入 actionType 模块
    import {
        CHANGE_INPUT_VALUE,
        ADD_TOTO_ITEM,
        DELETE_TODO_ITEM,
    } from './actionType'
    
    // 更改input处输入框里面的值
    export const getInputChangeAction = (value) => {
        return {
            type: CHANGE_INPUT_VALUE,
            value
        }
    }
    
    // 添加待办事项
    export const getAddItemAction = () => {
        return {
            type: ADD_TOTO_ITEM
        }
    }
    
    // 删除待办事项
    export const getDeleteItemAction = (index) => {
        return {
            type: DELETE_TODO_ITEM,
            index
        }
    }
    
    

    4.5 reducer 更新 state

    新建文件:/src/store/reducer.js

    import {
        CHANGE_INPUT_VALUE,
        ADD_TOTO_ITEM,
        DELETE_TODO_ITEM,
    } from './actionType'
    
    const defaultState = { // 默认一个state
        inputValue: 'abc',
        list: [1,2,3]
    };
    
    // 注意:reducer 可以接收state, 但是不允许修改state,所以需要深拷贝一份state
    export default (state = defaultState, action) => {
        console.log(state, action);
        if (action.type === CHANGE_INPUT_VALUE) {
            let newState = JSON.parse(JSON.stringify(state)); // 深拷贝一份state
            newState.inputValue = action.value;  // 更改拷贝后的state
            return newState;  // 将更新后的新的state 返回给 store
        }
        if (action.type === ADD_TOTO_ITEM) {
            let newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            return newState;
        }
        if (action.type === DELETE_TODO_ITEM) {
            let newState = JSON.parse(JSON.stringify(state));
            newState.list.splice(action.index, 1); // 删除当前index的item
            return newState;
        }
        return state;
    }
    

    4.6 修改 src/Todolist.js

    import React, { Component, Fragment } from 'react';
    import "antd/dist/antd.css";
    import { Input, Button, List } from 'antd';
    import store from './store/index';
    import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators';
    
    class TodoList extends Component {
    
      constructor(props) {
        super(props);
    
        this.state = store.getState();
    
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleBtnClick = this.handleBtnClick.bind(this);
    
        this.handleStoreChange = this.handleStoreChange.bind(this);
        // 订阅store,只要store中state有变化,就执行handleStoreState函数
        store.subscribe(this.handleStoreChange);
      }
    
      handleInputChange (e) { 
        const action = getInputChangeAction(e.target.value);
    
        // 将需要执行的action传给store
        store.dispatch(action);
      }
    
      handleBtnClick () {
        const action = getAddItemAction();
        store.dispatch(action);
      }
    
      handleItemDelete (index) {
        const action = getDeleteItemAction(index);
        store.dispatch(action);
      }
    
      handleStoreChange () {
        // 当store中state变化时,则重新取一次数据进行替换
        this.setState(store.getState());
      }
    
    
      render() {
        return (
          <Fragment>
            <div style={{padding: '10px'}}>
              <div>
                <Input
                  value={this.state.inputValue}
                  placeholder="input something"
                  style={{ '300px',marginRight: '10px'}}
                  onChange={this.handleInputChange}
                />
                <Button type="primary" onClick={this.handleBtnClick}>提交</Button>
              </div>
              <List
                style={{marginTop: '10px',  '300px'}}
                bordered
                dataSource={this.state.list}
                renderItem={
                  (item, index) => <List.Item onClick={this.handleItemDelete.bind(this, index)}>{item}</List.Item>
                }
              />
            </div>
          </Fragment>
        )
      }
    }
    
    export default TodoList;
    
    

    5 项目目录


  • 相关阅读:
    AB压力测试(Windows)
    Ensure You Are Not Adding To Global Scope in JavaScript(转)
    使用jasmine来对js进行单元测试
    HTML5安全:CORS(跨域资源共享)简介(转)
    asp.net+jquery Jsonp使用方法(转)
    在ios上时间无法parse返回 "Invalid Date"(转)
    用document.domain完美解决Ajax跨子域 (转)
    IE10、IE11 User-Agent 导致的 ASP.Net 网站无法写入Cookie 问题
    NodeJs:module.filename、__filename、__dirname、process.cwd()和require.main.filename 解惑(转)
    关于反射的一些总结(转)
  • 原文地址:https://www.cnblogs.com/cckui/p/11399321.html
Copyright © 2011-2022 走看看