preface
Continuation of the last chapter, I did not expect many readers so much attention, thank you for your support and suggestions, I just express my personal views and some of my own thinking may not be comprehensive enough, the use of Vue examples is only as a guide and Vue is also a high level of attention. Some friends would like to hear about other solutions besides Vuex. Today, we will start with Redux and gradually expand it (as the title).
A Brief Introduction to front-end State Management (PART 1)
Redux
As part of the React family, Redux tries to provide predictable state management for React applications. Like most state management schemes, Redux is based on a publish-subscribe model, so let’s take a quick look at Redux using libraries as an example.
The basic operation of Redux is roughly as follows:
- Store (Librarian)
- State (book)
- Action (Borrowing Order)
- Store. Dispatch
- Reducer (Package)
- Store. Subscribe (receive books)
Store (Librarian)
A Store can be thought of as a container, with only one Store for the entire application. It’s like you can only borrow books from the librarian.
import { createStore } from 'redux'
const store = createStore(reducer);
Copy the code
State (book)
For State, it can only be changed by Action. You should not change the value of State directly.
Use store.getState() to get the state.
import { createStore } from 'redux'
const store = createStore(reducer)
store.getState()
Copy the code
Action (Borrowing Order)
How do you want to borrow books? Submitting a library slip to the librarian, of course. Then the user can not access the State, can only operate through the View (such as click the button), that is, the change of State corresponding to the change of View, the View needs to submit an Action to notify the change of State. (It is only by submitting the library slip to the administrator that a series of other operations follow.)
Action is a custom object where the Type attribute is the Action that is agreed to be performed.
const action = {
type: 'click'.info: 'Submit a Borrowing form'
}
Copy the code
Store. Dispatch
Store. dispatch is the only way that a View can send an Action. It accepts an Action object (to submit a library order) and simply gives the order information to the librarian, who is searching for the appropriate book based on the order.
store.dispatch(action)
Copy the code
Reducer (Package)
After the Store receives an Action, it must present a new State so that the View can change, and the new State is calculated by Reducer. (Once you get the list, pack your books into bags, etc.)
Reducer is a custom function that takes Action and the current State as arguments and returns a new State.
const reducer = (state, action) = > {
return `action.info: ${state}` // => Submit the borrowing form: A Dream of Red Mansions
}
Copy the code
Store. Subscribe (receive books)
When State changes, store.subscribe() listens for the View to be automatically updated.
const unsubscribe = store.subscribe(() = > {
render() {
/ / update the view}})// You can also unsubscribe (listening)
unsubscribe()
Copy the code
summary
I believe that students who are new to Redux will find Redux tedious, which is also related to his idea that everything in Redux should be certain.
Although it is not possible to make everything deterministic in Redux (e.g. asynchronously), you should make sure that most parts are deterministic including:
- The rendering of the view is determinable
- State reconstruction is deterministic
As for why I did this, I mentioned it in the last article. It’s important for: easy application testing, error diagnosis, and Bug fixing.
Purpose of state management
The most common scenario that most programmers use Redux is to return from page A to page B and save the state of page B.
If the project is small, does Redux or Vuex seem too big? We know that keep-alive is provided in Vue to allow us to cache the current component, which can solve the above scenario.
Unfortunately, React doesn’t have a keep-alive like Vue does. The common solution in the community is to modify the route, but this modification is too intrusive for the project and difficult to maintain. In addition, route hooks were also eliminated in React-Router V5. Therefore, wrapping a function yourself is a good idea for small projects. (Of course, you can use Redux if you want, we’ll just explore more ways.)
Using the library as an example, you now have a library management system, and you jump from the list page to the detail page and need to save the status of the list page (such as the status of the search bar).
Let’s say you use react + ANTD to write a simple and crude version of the react stack:
// KeepAlive.js
export default function keepAliveWrapper() {
return function keepAlive(WrappedComponent) {
return class KeepAlive extends WrappedComponent { // ps
constructor(props) {
super(props)
// do something ...
}
componentDidMount() {
const {
keepAlive: { fieldsValue },
} = this.context
// do something ...
super.componentDidMount()
}
render() {
// do something ...
return super.render()
}
}
}
}
Copy the code
Here’s why the original component is inherited (// ps).
If the regular writing method returns a class component (Class KeepAlive extends react.component.ponent), it is essentially nested with parent and child components. The life cycle of the parent and child components is executed in order, so it is rendered twice each time it goes back to the list page to get the state. This is because the parent component returned by HOC calls the methods of the original component, causing the list page to be requested twice and rendered twice.
If HOC (high-level component) is inherited from the original component, it will not produce two life cycles to execute alternately, which is a good solution to this problem.
// main.jsx root component
import React from 'react'
const appContext = React.createContext()
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
keepAlive: {}, // Cache objects
isCache: false.// Whether to cache
fieldsValue: {} // Cache the form values}}componentDidMount() {
/ / initialization
const keepAlive = {
isCache: this.state.isCache,
toggle: this.toggleCache.bind(this),
fieldsValue: this.state.fieldsValue,
}
this.setState({ keepAlive })
}
// You can't set fields before render...
// For example list1 => list1/detail => list2 needs to put the jump in the following callback and clear the state
toggleCache(isCache = false, payload, callback) {
const { fieldsValue = null } = payload
const keepAlive = {
isCache,
fieldsValue,
toggle: this.toggleCache.bind(this),}const fn = typeof callback === 'function' ? callback() : void 0
this.setState(
{
keepAlive,
},
() = > {
fn
}
)
}
render() {
const { keepAlive } = this.state
<appContext.Provider value={{ keepAlive }}>
// your routes...
</appContext.Provider>
}
}
Copy the code
As for why you don’t use the context directly and wrap a layer of keepAlive around it, the reason is to keep the context in the same place. Using a decorator (@keepalive) in the header of the component immediately tells you that it is a cached component (easy to read and maintain).
// when the page is used
import React from 'react'
import keepAlive from '.. /keepAlive'
// The keepAlive position should be placed closest to the original component
@keepAlive()
class App extends React.Component {
constructor(props){
super(props)
this.state = {
// init something...}}componentDidMount() {
// do something...
if(this.context.keepAlive.fieldsValue) {
const { tableList } = this.context.keepAlive.fieldsValue
console.log('Cache:',tableList) // cache: ['1', '2']}}// View details
detail = () = > {
this.context.keepAlive.fieldsValue = {
tableList: ['1'.'2']}// jump...
}
// When you need to jump across a route, such as list1 => list1/detail (this method should be in the detail page) => list2, you need to deal with the warning
toList2 = () = > {
this.context.keepAlive.toggle(false, {}, () = > {
// jump...}}})Copy the code
In the above use of decorator writing method, briefly, need to configure the following Babel put to use
npm install -D @babel/plugin-proposal-decorators
In jsconfig.json (new if none) :
{
"compilerOptions": {"experimentalDecorators": true
},
"exclude": [
"node_modules"."dist"]}Copy the code
Configure in.babelrc:
{
"plugins": [
"@babel/plugin-proposal-decorators",
{
"legacy": true}}]Copy the code
The above method is suitable for the scenario mentioned above (returning from page A to page B requires saving the state of page B). Some people say that you should use Redux or Mobx instead. Cross-route jumps also have to be manually cleared to prevent warnings… That’s a matter of opinion. Self encapsulation also shows that they have research, whether he is easy or difficult, programming itself should not be constantly exploring, ha ha. While your writing may not be good enough or anything like that, just accept the criticism. There are plenty of great people out there.
The last
I have tried to write detailed, but it is difficult to adjust to all tastes, please big guy light spray ~
All see this, do not like to pay attention to (or give some advice) before leaving?
React is only used as a guide to expand the following series of problems, just like Vue in the previous part.
Say again now front end two big popular frameworks are not these two (of course JS foundation can not fall).
Finally put the last article again, let us review ~
A Brief Introduction to front-end State Management (PART 1)