Story synopsis

Let’s start with what Redux is, the three principles of Redux, and Redux’s core API, and explain how Redux works with React and how it changes over Flux.

What is the story

As we all know, Flux itself is neither a library nor a framework, but an architectural idea for applications. As for Redux, its core code can be understood as a library, but it also emphasizes architectural ideas similar to Flux. In terms of design, Redux references the design of Flux, but simplifies many redundant parts of Flux, while integrating the idea of functional programming. Interestingly enough, Redux started as an experiment. The author did not expect that Redux would become so important and widely used, but only to solve his problems of heat overload and time travel through Flux ideas. Redux itself is very simple, and its design idea is similar to React. It hopes to use the least API to achieve the most core functions. Redux itself positions itself only as a predictable container of state. The “Redux” itself refers to the NPM package Redux, which provides apis that let us create a store using reducer and update the data in the store or get the latest state in the store. The “Redux application” refers to a complete flux-like front-end application that uses the NPM package Redux and combines the view layer implementation (such as React) with other necessary components of the front-end application (routing library, request library, etc.).

Redux’s Three principles

To understand Redux, it is important to know the three principles of Redux design and use.

  • Single data Source In a traditional MVC framework, we can create as many models as we need, and models can listen to each other, trigger events, or even loop or nest trigger events, which are not allowed in Redux. Because in Redux’s mind, an application always has only one data source. Our first reaction might be: if you have a complex application, forcing a unique data source would result in a very large JavaScript object. In fact, the advantage of using a single data source is that the entire application state is stored in a single object, so that the entire application state can be extracted and persisted at any time. In addition, such a design opens up the possibility of server-side rendering. As for the problem that the data source object is too large, we can solve it through combineReducers.

  • State is read-only. In Redux, we don’t define a store ourselves in code. Instead, we define a Reducer, whose function is to iterate on the current application state according to the currently triggered actions. Here, we did not directly modify the application state, but returned a new state. The createStore method provided by Redux generates a store based on the Reducer. Finally, we can use the store.dispatch method to modify the state.

  • In Redux, we confirm the state modification by defining reducer, and each reducer is a pure function, which means that it has no side effects, that is, it will get some output if it accepts some input. The benefit of this design is not only that the modification of the state in reducer is simple, pure and testable, but more interesting is that Redux generates a cool time travel debugging mode using each newly returned state, making it possible to track the result of each state change due to the triggering of action.

Redux core API

At the heart of Redux is a store, which is generated by the createStore method provided by Redux. To generate a store, you must pass in the Reducers. In Redux, the reducer role is responsible for responding to actions and modifying data. Reducer is essentially a pure function with two parameters, state and action. The reducer’s job is to calculate the new state based on state and action. In practical applications, reducer also needs a special non-null judgment when dealing with state. Obviously, there is no state when the Reducer is first executed, and the final duty of the Reducer is to return new state, so a defined state needs to be returned in this special case. So that’s why we’re going to define a defaultState. Here’s a look at Redux’s core API, createStore:

import { createStore } from 'redux';
const store = createStore(reducers);
Copy the code

The store created by the createStore method is an object that itself contains four methods.

  • GetState () : Gets the current state of the store.
  • Dispatch (Action) : Dispatch an action and return the action. This is the only way to change the data in the store.
  • Subscribe (listener) : Registers a listener that is called when the store changes.
  • ReplaceReducer (nextReducer) : Updates the reducer in the current store. This reducer is usually called only in the development mode. In real development, the two methods we use most often are getState() and Dispatch (). The subscribe() and replaceReducer() methods are typically used when Redux Bridges to a system.

Simple project practices

In current projects, modular development has become mainstream. In my opinion, the most important thing in modular development is the design of directory structure.

Here we have a more detailed division of the catalog:

  • Components: This directory holds components, such as Header public components, which are often used in projects that develop web classes.
  • Pages: This directory stores web pages. Each web page in the system is stored in this directory. For example, home page and details page.
  • Store: This directory holds Actions, reducers, Constants (constant file: holds constants used by Dispatch) and index(creates store).

In my opinion, in the Redux project, such directory design is more in line with the idea of modular design. Next, I’ll go through the steps and processes of Redux. (Simple list data has been requested)

Step 1: Reducer

Data in the store is determined by the reducer, so the first step is to complete the reducer.

const defaultState = {
  homeList: []
}
export default function(state = defaultState, action) {
  switch(action.type) {
    case GET_HOME_LIST:
      return {
        homeList: action.homeList
      };
    default:
      return defaultState
  }
}
Copy the code

Step 2: Action

export const getHomeList = {
  type: GET_HOME_LIST, // simulate false data received from the back end homeList: [0,1,2,3]}Copy the code

Step 3: Constant

An application’s constants should be unique, and keeping constants in a folder and grouped by module helps prevent variable name conflicts.

// home
export const GET_HOME_LIST = 'GET_HOME_LIST';
Copy the code

Step 4: Index

This file is used to create a store.

import { createStore, 
  combineReducers} from 'redux'
import HomeReducer from './reducers/home/index'; // Merge multiple reducers into a const rootReducer = combineReducers({home: HomeReducer}) // Create a reducer store can only receive a reducerexport default createStore(rootReducer)
Copy the code

Step 5: Use store

class App extends ImmutableComponent {
  render() {
    return<Provider store={store}> <BrowserRouter> <Header /> <Route path="/" component={Home} exact/>
            <Route path="/detail"component={Detail} /> </BrowserRouter> </Provider> ); }}Copy the code
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getHomeList } from '.. /.. /store/actions/home';
class Home extends Component {
  componentDidMount() {
    this.props.fetchHomeList();
  }
  state = {}
  render() {
    return( <div> home length: { this.props.homeList.length } </div> ); }} // state: the entire store,home page, as long as the home module, filter the result (returnConst mapStateToProps = (state) => {return{ homeList: State.homelist}} // The user is operating on the UI causing a page change // Initiate an action // dispacth connect to the component const mapDispatchToProps = (dispatch) => {return {
    fetchHomeList() {
      dispatch(getHomeList)
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Copy the code