React中如何使用Redux管理数据

内容简介

本内容是基于实实在在的项目开发提炼出来的解决方案,手把手教你如何在React中使用Redux来进行数据管理,该案例可以直接在项目中使用;本文中不会介绍原理什么之类的,网上一大堆。本内容适合有react开发基础的人学习,希望对大家有帮助。

目录结构

  • 1 按类型分
    这里的类型指的是一个文件在项目中充当的角色类型,即这个文件是一个component,还是一个container,或者是一个reducer等,充当component、container、action、reducer等不同角色的文件,分别放在不同的文件夹下,这也是Redux官网示例所采用的项目结构。这种结构如下所示:
    actions/
    a.js
    b.js
    components/
    a1.jsx
    a2.jsx
    b1.jsx
    constainers/
    a.jsx
    b.jsx
    reducers/
    a.js
    b.js
    index.js复制代码
    当项目比较小时按类型来分是完全没问题的,当项目越来越庞大你会发现要修改某数据结构时很麻烦。
  • 2 按功能分
    我们在项目开发中总结了一种比较合适的目录结构,components里存放所有的view组件和组件对应的样式文件,constainers里存放所有的容器组件,为了开发效率和减少文件数量,我们把容器组件的action和reducer放在同一个文件(XXXRedux.js)里面。这种结构如下所示::
    components/
    componentA/
      a.jsx
      a.less
    componentB/
      b.jsx
      b.less
    constainers/
    constainer1/
       X.jsx
       X.less
       XRedux.js
    constainer2/
       XX.jsx
       XX.less
       XXRedux.js复制代码
    显而易见采用这样的目录结构要修改某一个组件时很方便,每一个容器组件对应的action、reducer、component、css都同一个文件目录下,操作很方便。

    开发步骤

    因为涉及到公司数据安全问题,真实项目是没法给各位看官look look了;以下都用经典的todolist代码做展示。
  • 1 设计reducer
    设计store有很多讲究,不能把服务给的数据结构直接作为store,也不能按照view结构来设计store;对于初学者来说是一件很烦恼的事,社区很多都推荐按照设计数据库表结构来设计store,但是很多以项目交付为目标的不可能做得这么标准,所以大家都尽量向标准看齐-_-。
    曾经在文章看到设计store的标准,给大家参考参考:

    • 1) 把整个应用的状态按照领域(Domain)分成若干子State,子State之间不能保存重复的数据。
    • 2) State以键值对的结构存储数据,以记录的key/ID作为记录的索引,记录中的其他字段都依赖于索引。
    • 3)State中不能保存可以通过已有数据计算而来的数据,即State中的字段不互相依赖。

    在业务比较复杂的情况下强力建议大家使用immutable,在项目初期还没有感觉,但是项目越来越大,业务越来越复杂时就能完全感受到它的好处了。
    以下代码是一个reducer文件,我们把该reducer需要的所有type定义在一个常量types对象中,initialState是最初state没有数据时显示的默认数据。

const types = {
  SELECT_ENTRY: 'select_entry',
  CREATE_NEW_ENTRY: 'create_new_entry',
  EDIT_ENTRY: 'edit_entry',
  CANCEL_EDIT: 'cancel_edit',
  UPDATE_ENTRY_LIST: 'update_entry_list',
  UPDATE_SAVED_ENTRY: 'updadte_saved_entry'
}

const initialState = {
  selectedId: null,
  isEditing: false,
  big: '测试',
};

export default function editor(state = initialState, action) {
  switch (action.type) {
    case types.SELECT_ENTRY:
      return Object.assign({}, state, { selectedId: action.id });
    case types.CREATE_NEW_ENTRY:
      return Object.assign({}, state, { selectedId: null, isEditing: true });
    case types.EDIT_ENTRY:
      return Object.assign({}, state, { selectedId: action.id, isEditing: true });
    case types.CANCEL_EDIT:
      return Object.assign({}, state, { isEditing: false });
    default:
      return state;
  }
}复制代码
  • 2 action
    为了快速开发,方便修改和减少文件数量,把action也写在reducer里面;一般每reducer里面代码最多也不会超过400行,如果代码量太多你应该考虑你组件是否写得有问题了;大家把storage操作就当作是与后台服务的交互吧。
    可能已有人注意到使用dispatch分发action时已经使用异步数据流了,是的,但是使用异步操作之前还是需要在Redux里添加一些异步操作中间价,不用捉鸡,这个后面代码会讲到。

    (注意⚠️)在真正的项目开发时候因为业务需要,所以store没有这么简单;很多时候交互都是在props里获取的对象(变量非引用无所谓),若要对从props里获取的对象进行某些业务操作时一定要深拷贝一份进行操作,否则你会遇到意想不到的结果。

export function selectEntry(id) {
  return { type: types.SELECT_ENTRY, id };
}

export function createNewEntry() {
  return { type: types.CREATE_NEW_ENTRY };
}

export function editEntry(id) {
  return { type: types. EDIT_ENTRY, id };
}

export function cancelEdit() {
  return { type:  types.CANCEL_EDIT };
}

function updateEntryList(items) {
  return { type:  types.UPDATE_ENTRY_LIST, items };
}

export function deleteEntry(id) {
  return dispatch => {
    storage.deleteEntry(id)
    .then(() => storage.getAll())
    .then((items) => dispatch(updateEntryList(items)));
  };
}

export function fetchEntryList() {
  return dispatch => {
    storage.getAll()
      .then(items => dispatch(updateEntryList(items)));
  };
}

function updateSavedEntry(id) {
  return { type: types.UPDATE_SAVED_ENTRY, id };
}

export function saveEntry(item) {
  const { title, content, id } = item;
  return dispatch => {
    if (id) {
      // 更新流程
      storage.updateEntry(id, title, content)
        .then(() => dispatch(updateSavedEntry(id)))
        .then(() => storage.getAll())
        .then(items => dispatch(updateEntryList(items)));
    } else {
      // 创建流程
      storage.insertEntry(title, content)
        .then(inserted => dispatch(updateSavedEntry(inserted.id)))
        .then(() => storage.getAll())
        .then(items => dispatch(updateEntryList(items)));
    }
  };
}复制代码
  • 3 Redux 与组件
    在项目中使用Redux做数据管理时,我们把组件分为容器组件和展示组件,一般容器组件就负责数据是这么更新的,不会包含任何Virtual DOM的修改和组合以及组件样式;而展示组件是view相关的,一般会写成无状态组件,但是在项目中很多时候展示组件需要有生命周期,但是建议尽量使用无状态组件。

    import React, { PropTypes } from 'react';
    import { connect } from 'react-redux';
    import CreateBar from '../CreateBar';
    import List from '../List';
    import ItemShowLayer from '../ItemShowLayer';
    import ItemEditor from '../ItemEditor';
    
    import './style.scss';
    /**
       * Deskmark是一个容器组件
       */
    class Deskmark extends React.Component {
      constructor(props) {
        super(props);
      }
      static defaultProps = {} 
    
      static propTypes = {
        state: PropTypes.object.isRequired,
        actions: PropTypes.object.isRequired,
      }
      componentDidMount() {
        this.props.actions.fetchEntryList();
      }
    
      render() {
        const { state, actions } = this.props;
        const { isEditing, selectedId } = state.editor;
        const items = state.items;
        const item = items.find(
          ({ id }) => id === selectedId
        );
    
        const mainPart = isEditing
          ? (
            <ItemEditor
              item={item}
              onSave={actions.saveEntry}
              onCancel={actions.cancelEdit}
            />
          )
          : (
            <ItemShowLayer
              item={item}
              onEdit={actions.editEntry}
              onDelete={actions.deleteEntry}
            />
          );
    
        return (
          <section className="deskmark-component">
            <nav className="navbar navbar-fixed-top navbar-dark bg-inverse">
              <a className="navbar-brand" href="#">Deskmark App</a>
            </nav>
            <div className="container">
              <div className="row">
                <div className="col-md-4 list-group">
                  <CreateBar onClick={actions.createNewEntry} />
                  <List
                    items={items}
                    onSelect={actions.selectEntry}
                  />
                </div>
                {mainPart}
              </div>
            </div>
          </section>
        );
      }
    }
    /**
     * mapStateToProps可以过滤该组件需要的数据
     * @param {*} state Redux管理的store
     */
    function mapStateToProps(state) {
      const { state, actions } = state;
      return {
        state,
        actions
      };
    }
    export default connect(mapStateToProps)(Deskmark);复制代码
  • 4 入口文件
    app.js文件是项目的入口文件,也是在React中使用Redux的最关键的一步,该文件只需要配置一次,以后都可以拿来直接用。

    插件说明

    • 1) redux
    • 2) react-redux 该插件让Redux在React中的使用爽得不要不要的,在React中只需要使用一个组件(Provider)和一个高价函数(connect)就可以用Redux来管理React的数据了。
    • 3) react-router 把router的数据给Redux管理
    • 4) redux-thunk 可以在Redux中使用异步操作,网上也有很多类似比较牛X异步中间价

    大家如果有兴趣可以看看redux-arena,项目中没有使用过,也就不做过多介绍了。

    • 1) app.js

      import React from 'react';
      import ReactDom from 'react-dom';
      import {Provider} from 'react-redux';
      import Deskmark from '../containers/Main/index';
      import mainStore from '../containers/Main/store';
      import { Router, Route, hashHistory, browserHistory }       from 'react-router';
      //获取store
      const store = mainStore();
      
      ReactDom.render(
        <Provider store={store}>
            <Router history={hashHistory}>
                <Route path="/" component={Deskmark} />
            </Router>
        </Provider>,
        document.getElementById('react-content')
      );复制代码
    • 2) store.js

      import { createStore, applyMiddleware, compose } from 'redux';
      import thunkMiddleware from 'redux-thunk';
      import rootReducer from './mainReducer';
      
      //thunkMiddleware异步操作中间件
      const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
      //创建一个store
      export default function mainStore() {
        const store = createStoreWithMiddleware(rootReducer, compose(applyMiddleware(thunkMiddleware))
         );
      
        return store;
      }复制代码
    • 3) mainReducer.js

      import {
        combineReducers
      } from 'redux';
      
      //combineReducers把多个子reducer合并成一个reducer
      const rootReducer = combineReducers({
        editor,
        index,
        items
      });
      
      export default rootReducer;复制代码

好了,到此为止在React中使用Redux就告一段落,谢谢阅读,如果有问题可以一起讨论,若文中有错误欢迎批评指出或者给出建议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值