Content abstract

This content is a solution extracted based on real project development. Hand in hand, it teaches you how to use Redux for data management in React. This case can be directly used in the project. I’m not going to tell you how it works in this article, there’s a lot of it on the Internet. This content is suitable for people who have the basis of react development to learn, hope to help you.

The directory structure

  • 1 by type

    The type refers to the type of role that a file plays in a project, i.e. whether the file is a Component, a Container, or a reducer, etc., which acts as a component, container, action, reducer, etc. In separate folders, this is also the project structure used by the Redux website example. The structure is as follows:
    actions/
    a.js
    b.js
    components/
    a1.jsx
    a2.jsx
    b1.jsx
    constainers/
    a.jsx
    b.jsx
    reducers/
    a.js
    b.js
    index.jsCopy the code

    Sorting by type is perfectly fine when the project is small, but as the project gets bigger and bigger you find it difficult to change a data structure.

  • 2 points by function

    In order to reduce the number of files and improve the efficiency of development, we concluded that a directory structure would be suitable. Components would store all view components and their corresponding style files, and Constainers would store all container components. We put the action and reducer of the container components in the same file (xxxredux.js). The structure is as follows:
    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.jsCopy the code

    Obviously, this directory structure is very convenient to modify a component. The action, Reducer, Component, and CSS corresponding to each container component are all in the same file directory, which is very easy to operate.

    Development steps

    Because of the company’s data security issues, the real project is not to give you look look; The following are shown in classic Todolist code.

  • 1. There are many requirements in designing the Reducer Store. The data structure given by the service cannot be directly used as the store, nor can the Store be designed according to the view structure. For beginners is a very annoying thing, the community is recommended to design store according to the design database table structure, but many project delivery goals can not do so standard, so we try to keep up with the standard -_-. I have seen the standard of design Store in the article, for your reference:

    • 1) Divide the State of the entire application into several sub-states according to Domain, and repeat data cannot be saved among sub-states.
    • 2) State stores data in a key-value pair structure. The key/ID of the record is used as the index of the record. All other fields in the record depend on the index.
    • 3) State cannot hold data that can be calculated from existing data, that is, the fields in State do not depend on each other.

    It is strongly recommended that we use immutable in the case of more complex business. It does not feel at the beginning of the project, but it can be fully felt as the project gets bigger and more complex. The following code is a reducer file. We define all types required by the reducer in a constant object called types. InitialState is the default value displayed if the original state has no data.

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: 'test'};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:
      returnstate; }}Copy the code
  • 2 action

    In order to reduce the number of files and reduce the speed of development, action is also written in reducer. There should be no more than 400 lines of code per reducer. If there is too much code, you should consider whether you have problems writing components. Think of the storage operation as the interaction with the background service.

    Some of you may have noticed that you’re already using asynchronous data streams when you dispatch an action. Yes, but you still need to add some asynchronous midpoints to your Redux before you can use them.

    (note ⚠️) In real project development because of business needs, so store is not so simple; A lot of interactions are done with props objects. If you want to perform operations on props objects, make sure to make a deep copy. Otherwise, you might encounter unexpected results.

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 => {
    ifStore.updateentry (id, title, content) .then(() => dispatch(updateSavedEntry(id))) .then(() => storage.getAll()) .then(items => dispatch(updateEntryList(items))); }else{// create process store.insertentry (title, content) .then(inserted => dispatch(updateSavedEntry(inserted.id))) .then(() => storage.getAll()) .then(items => dispatch(updateEntryList(items))); }}; }Copy the code
  • When Redux is used for data management in a project, we divide components into container components and presentation components. Generally, container components are responsible for data updating in this way, and do not include any modification, composition and component style of Virtual DOM. Display components are view-related and are usually written as stateless components, but many times in a project display components need to have a life cycle, but it is recommended to use stateless components whenever possible.

    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 extends React.Com. Ponent {constructor(props) {super(props); /** * Deskmark extends React.Com. } 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 Filters data required by this component * @param {*} state Stores managed by Redux */function mapStateToProps(state) {
      const { state, actions } = state;
      return {
        state,
        actions
      };
    }
    export default connect(mapStateToProps)(Deskmark);Copy the code
  • App. js file is the entry file of the project, and also the most critical step in using Redux in React. This file only needs to be configured once, and can be used directly in the future.

    Plugin instructions

    • 1) redux
    • 2) react – story the plug-in let the story in the use of the react cool don’t don’t of, only need to use a component in the react (Provider) and a high function (connect) story will be used to manage the react of the data.
    • 3) React-router gives router data to Redux to manage
    • 4) Redux-Thunk can use asynchronous operations in Redux, and there are many similar asynchronous midpoints online

    If you are interested, you can check out the Redux-Arena, which is not used in the project, so I won’t talk about it too much.

    • 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'; / / Get store const Store = mainStore(); ReactDom.render( <Provider store={store}> <Routerhistory= {hashHistory}>
                <Route path="/" component={Deskmark} />
            </Router>
        </Provider>,
        document.getElementById('react-content'));Copy the code
    • 2) store.js

      import { createStore, applyMiddleware, compose } from 'redux';
      import thunkMiddleware from 'redux-thunk';
      import rootReducer from './mainReducer'; / / thunkMiddleware asynchronous operations middleware const createStoreWithMiddleware = applyMiddleware (thunkMiddleware) (createStore); / / Create a storeexport default function mainStore() {
        const store = createStoreWithMiddleware(rootReducer, compose(applyMiddleware(thunkMiddleware))
         );
      
        return store;
      }Copy the code
    • 3) mainReducer.js

      import {
        combineReducers
      } from 'redux'; CombineReducers ({editor, index, items}); combineReducers({editor, index, items}); combineReducers({editor, index, items});export default rootReducer;Copy the code

Thank you for reading. If you have any questions, we can discuss them together. If there are mistakes in the article, you are welcome to point out your criticism or give suggestions.