Hello, long time no more, today we talk about the history of front-end state management ~

preface

We know that in the early days of front-end development, HTML pages were static, and any small change meant a new page; After the emergence of IFrame and XMLHttpRequest, the realization of asynchronous local loading, greatly improve the user experience; JQuery uses imperative programming to manage the state of the DOM, but once the application is complex, it is difficult to maintain; In recent years, from the birth of Angular to React, Vue has been launched one after another, with various front-end technology frameworks emerging one after another. In addition to Vuex, React and Redux that Vue users are familiar with, Mobx, Rxjs and other excellent data storage and management solutions have emerged. This is where the concept of front-end state management really comes into play.

It has to be said that the emergence of SPA further drives the separation of the front and back ends. The front end no longer needs to display the page by requesting the HTML returned by THE URL, but generates DOM through loading and parsing JS code, and independently realizes the rendering of the content. In this process, the idea of data-driven is also deeply rooted in people’s hearts.

By data-driven, we mean that the view is generated by data, and we can manipulate the DOM by modifying the data, which is also typical of declarative programming. Take React as an example, we can easily update the UI by changing the state. However, there is a problem. React does not have any restrictions on the data layer, that is, any component can change the code of the data layer, which brings a problem. So how do you manage common state in view components?

At this time, the Facebook team put forward the Flux idea, aiming to solve the problems of MVC’s increasingly complex internal logic in complex scenarios from the architectural level. So what exactly is the Flux architecture?

Flux idea and its implementation

An interesting statement from the Vuex documentation reads: “Flux architecture is like glasses: you’ll know when you need it.”

Ok, still not clear, let’s first understand some basic concepts of Flux. (Take Facebook’s official implementation as an example)

Flux divides an application into four parts: the View layer View, the message Action sent by the View layer, the Store for receiving Actions and executing the callback function Dispatcher, and the Store for storing the state of the application.

As you can see from the figure above, this is a one-way flow: users can only trigger actions to change the state, and the state of the application must be managed in the Store, listening for specific actions. Any state change is initiated by Action and dispatched by Dispatcher, so we can easily record every state change. This is also the basic design concept of Flux, and more and more Flux practices have emerged on this basis. Next, we will take the development history of React state management as an example to see how Redux manages state.

(If you are interested in Flux, follow up on This Flux architecture primer by Yifeng Ruan.)

Redux

When React was first introduced, there was no Redux. We managed component states only through state, which was sufficient in most cases, but once applications got complicated, state became difficult to maintain. Then Flux came into being, and in this environment Redux was born.

First, Redux has three principles: single data source, state read-only, and use pure functions to perform modifications. React follows the principle of data immutability, that is, never modify properties on the original object. In the source code, Redux only compares the storage locations of the old and new objects to see if they are the same. This means that the properties of the old and new objects cannot be changed directly. Only a new state can be returned each time. So we can think of Redux as Reduce + Flux, and Reduce is the pure function mentioned above.

Not only that, but another important concept in Redux is Middleware.

The middleware in Redux provides extension points after the Action is initiated and before the Reducer is arrived.

It can be seen that when using middleware, the middleware will process the Actions initiated and finally turn them over to Reducer for execution. Suppose we needed to print the log every time an Action was triggered, we could rewrite the Dispatch method, something like this:

let next = store.dispatch
store.dispatch = function dispatchLog(action) {  
  console.log('log', action) 
  next(action) 
}
Copy the code

This way, you don’t need to do any extra work later when you issue an Action.

Middleware = (store) => (next) => (action) => {[return next(action)]} The applyMiddleware method is used to add middleware, and the core code looks something like this:

let store = createStore(reducers);
let dispatch;
Chain [f(next)=>acticon=>next(action)]
// This function receives the next argument, which is the Diapatch method passed between middleware
let chain = middlewares.map(middleware= > middleware({
  getState: store.getState, // Because the store does not update when the closure executes middleware
  dispatch: action= > dispatch(action)
}));
// Call the native dispatch override dispatch logic and only the last middleware will accept the actual dispatch methoddispatch = compose(... chain)(store.dispatch);return {
  ...store,
  dispatch
}

Copy the code

We can see that the design of the middleware is really clever. The curvy structure can easily access the same store, and can also be used in conjunction with the compose method to accumulate parameters to achieve the effect of delayed execution. The description of the compose method will not be listed, but mainly integrates the middleware from right to left through the reduce method.

In fact, there are other aspects of the Redux source code that are worth pondering, and I’ll write about them later. Next, we’ll look at a fresh face.

The next generation React state manager

Since React16, everyone has been using Hooks, which, to a certain extent, solve the problem of reusing functionality between components. This encapsulation and reusing of logic is nice, but there are still some issues, such as data sharing. Hox, called the next generation React state Manager, is designed to solve just this kind of problem.

Enter the project on Github, where the Features are interesting: there is only one API; Use Custom Hooks to define models; Embrace TypeScript perfectly; Support for multiple data sources. Let’s take a look at the official example:

/ / define the Model
import { createModel } from 'hox';
/* Any custom Hook */
function useCounter() {
  const [count, setCount] = useState(0);
  const decrement = (a)= > setCount(count - 1);
  const increment = (a)= > setCount(count + 1);
  return {
    count,
    decrement,
    increment
  };
}
export default createModel(useCounter)
--------------------------------------------------------------------------------------------
/ / using the Model
import { useCounterModel } from ".. /models/useCounterModel";

function App(props) {
  const counter = useCounterModel();
  return (
    <div>
      <p>{counter.count}</p>
      <button onClick={counter.increment}>Increment</button>
    </div>
  );
}
// At this point useCounterModel is a real Hook that subscribs to data updates. That is, when the 'Increment' button is clicked, an update to the CounterModel is triggered and all components or hooks that use the useCounterModel are eventually notified.
Copy the code

The createModel here is similar to the use of HOC to enable the sharing of data, presumably by implementing a publish subscriber within the Model that will notify its subscribers to re-render whenever the Model is re-rendered. Interested children might as well go directly to see the source code, its core content is not much.

(Hox is an API that implements global data sharing, rather than the Github address createStore.)

conclusion

From the origin of front-end state management to the analysis of React implementation process, I simply share some of my own views. If there is not enough accuracy in the article, we welcome to exchange and correct.

Finally, 520 happy, thanks for reading!