Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Go deep into the Redux architecture

2025-01-21 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)06/01 Report--

About redux

I have written an article about understanding Redux through a demo, but there is no in-depth analysis of the core methods of redux. I will summarize and learn again here. The complete code can be seen here. (refer to a series of tutorials on React technology stack)

When do I need to use redux?

The way users use it is complicated.

Users with different identities are used in different ways (such as ordinary users and administrators)

Multiple users can collaborate

Interact heavily with the server, or use WebSocket

View needs to get data from multiple sources

To put it simply, if your UI layer is very simple and doesn't have a lot of interaction, Redux is unnecessary and adds complexity. Multi-interaction, multi-data source scenarios are more suitable to use Redux.

Design ideas:

The Web application is a state machine, and the view corresponds to the state one by one.

All the states are saved in one object.

Redux workflow:

First, the user issues an Action.

Store.dispatch (action)

Store then automatically calls Reducer, passing in two parameters: the current State and the received Action. Reducer returns the new State.

Let nextState = todoApp (previousState, action)

Whenever there is a change in State, Store calls the listening function.

/ / set the listening function store.subscribe (listener)

Listener can get the current state through store.getState (). If you are using React, you can trigger a re-rendering of View.

Function listerner () {let newState = store.getState (); component.setState (newState);}

If you don't understand the above process now, don't worry, after reading the following API, you can almost understand the core mechanism of Redux.

Back to the top (go to top)

API

Store

Store is the place where data is stored, and you can think of it as a container. There can be only one Store for the entire application.

Redux provides the function createStore, which is used to generate Store.

In the following code, the createStore function takes another function as an argument and returns the newly generated Store object.

Import {createStore} from 'redux';const store = createStore (fn)

State

The Store object contains all the data. If you want to get data from a certain point in time, you need to take a snapshot of the Store. The collection of data at this point in time is called State.

The State of the current moment can be obtained through store.getState ().

Import {createStore} from 'redux';const store = createStore (fn); const state = store.getState ()

Redux stipulates that one State corresponds to one View. As long as the State is the same, View is the same. You know State, you know what View is like, and vice versa.

Action

The change of State will lead to the change of View. However, users do not have access to State, only View. Therefore, the change in State must be caused by View. Action is a notification from View that State should change.

Action is an object. The type attribute is required and represents the name of the Action. Other properties can be set freely, and the community has a specification to refer to.

Const action = {type: 'ADD_TODO', payload:' Learn Redux'}

In the above code, the name of Action is ADD_TODO, and the information it carries is the string Learn Redux.

It can be understood that Action describes what is happening right now. The only way to change State is to use Action. It will deliver the data to Store.

Action Creator

There will be as many Action as View sends. If they are all handwritten, it will be troublesome. You can define a function to generate Action, which is called Action Creator.

Const ADD_TODO = 'add TODO';function addTodo (text) {return {type: ADD_TODO, text}} const action = addTodo (' Learn Redux')

Store.dispatch ()

Store.dispatch () is the only way for View to issue Action.

Import {createStore} from 'redux';const store = createStore (fn); store.dispatch ({type:' ADD_TODO', payload: 'Learn Redux'})

In the above code, store.dispatch takes an Action object as a parameter and sends it out.

Combined with Action Creator, this code can be rewritten as follows.

Store.dispatch (addTodo ('Learn Redux'))

Reducer

After Store receives the Action, a new State must be given so that the View will change. The process of calculating this State is called Reducer.

Reducer is a function that takes Action and the current State as arguments and returns a new State. Here is a practical example

Const defaultState = 0action.payload; default Const reducer = (state = defaultState, action) = > {switch (action.type) {case 'ADD': return state + action.payload; default: return state;}}; const state = reducer (1, {type:' ADD', payload: 2})

In the above code, when the reducer function receives an Action named ADD, it returns a new State as the result of the addition. The logic of other operations (such as subtraction) can also be implemented according to Action.

In practical application, the Reducer function does not have to be called manually as above, and the store.dispatch method will trigger the automatic execution of the Reducer. To do this, Store needs to know the Reducer function by passing Reducer into the createStore method when the Store is generated.

Import {createStore} from 'redux';const store = createStore (reducer)

In the above code, createStore takes Reducer as a parameter to generate a new Store. Every time a new Action is sent by store.dispatch, Reducer is automatically called to get a new State.

Store.subscribe ()

Store allows you to set a listener function using the store.subscribe method, which is automatically executed whenever the State changes.

Import {createStore} from 'redux';const store = createStore (reducer); store.subscribe (listener)

Obviously, as long as you put the update function of View (for React projects, the render method or setState method of the component) into listen, you will achieve automatic rendering of View.

The store.subscribe method returns a function that can be called to delisten.

Let unsubscribe = store.subscribe (()) = > console.log (store.getState ()); unsubscribe ()

Back to the top (go to top)

Middleware and Asynchronous Operation

A key question remains unsolved: what about asynchronous operations? Immediately after the Action is issued, Reducer calculates the State, which is called synchronization; after the Action is issued, the Reducer is executed for a period of time, which is asynchronous.

How can Reducer execute automatically after an asynchronous operation ends? This requires the use of new tools: middleware (middleware).

In order to understand middleware, let's think about it from the perspective of the framework author: if you want to add functionality, in which part will you add it?

(1) Reducer: pure function, which only undertakes the function of calculating State. It is not suitable to undertake other functions and cannot bear it, because in theory, pure function cannot read or write.

(2) View: corresponding to State one by one, it can be regarded as the visual layer of State, and it is not suitable to undertake other functions.

(3) Action: the object that stores the data, that is, the carrier of the message, can only be operated by others, and cannot do anything on its own.

After thinking about it, only this step of sending Action, the store.dispatch () method, can add functionality.

The usage of middleware

This article does not involve how to write middleware, because commonly used middleware is readily available, as long as you reference modules written by others. For example, the logging middleware in the previous section has an off-the-shelf redux-logger module. Here is only how to use middleware.

Import {applyMiddleware, createStore} from 'redux';import createLogger from' redux-logger';const logger = createLogger (); const store = createStore (reducer, applyMiddleware (logger))

In the above code, redux-logger provides a generator createLogger that can generate the logging middleware logger. Then, put it in the applyMiddleware method, pass in the createStore method, and complete the enhancement of store.dispatch ().

Here are two points to note:

(1) the createStore method can accept the initial state of the entire application as a parameter, in which case applyMiddleware is the third parameter.

Const store = createStore (reducer, initial_state, applyMiddleware (logger))

(2) the order of middleware is exquisite.

Const store = createStore (reducer, applyMiddleware (thunk, promise, logger))

In the above code, the three parameters of the applyMiddleware method are three middleware. Some middleware have sequence requirements, so check the document before using it. For example, logger must be placed last, otherwise the output will be incorrect.

Back to the top (go to top)

The basic idea of Asynchronous Operation

Once you understand the middleware, you can handle asynchronous operations.

Synchronous operations only need to issue one kind of Action. The difference between asynchronous operations is that it emits three kinds of Action.

Action when the operation was initiated

Action when the operation is successful

Action if the operation fails

Take fetching data from the server as an example, three Action can be written in two different ways.

/ / method 1: the name is the same Different parameters {type: 'FETCH_POSTS'} {type:' FETCH_POSTS', status: 'error', error:' Oops'} {type: 'FETCH_POSTS', status:' success', response: {...}} / / two: different names {type: 'FETCH_POSTS_REQUEST'} {type:' FETCH_POSTS_FAILURE', error: 'Oops'} {type:' FETCH_POSTS_SUCCESS' Response: {...}}

In addition to the different types of Action, the State of asynchronous operation also needs to be modified to reflect different operation states. Here is an example of State.

Let state = {/ /... IsFetching: true, didInvalidate: true, lastUpdated: 'xxxxxxx'}

In the above code, the property isFetching of State indicates whether the data is being crawled. DidInvalidate indicates whether the data is out of date, and lastUpdated indicates when it was last updated.

Now, the whole idea of asynchronous operation is clear.

At the beginning of the operation, an Action is sent, which triggers the State to be updated to the "operating" state, and the View renders again.

After the operation is over, another Action is sent, which triggers the State to update to the "operation ended" status, and the View renders again.

Redux-thunk middleware

Asynchronous operations must send at least two Action: the user triggers the first Action, which is the same as the synchronous operation, and there is no problem; how can the system automatically send the second Action at the end of the operation?

The secret lies in Action Creator.

Class AsyncApp extends Component {componentDidMount () {const {dispatch, selectedPost} = this.props dispatch (fetchPosts (selectedPost))} / /.

The above code is an example of an asynchronous component. After loading successfully (the componentDidMount method), it sends out (the dispatch method) an Action that asks the server for the data fetchPosts (selectedSubreddit). The fetchPosts here is Action Creator.

Here is the code for fetchPosts, and the key point is in it.

Const fetchPosts = postTitle = > (dispatch, getState) = > {dispatch (requestPosts (postTitle)); return fetch (`/ some/API/$ {postTitle} .json`) .then (response = > response.json ()) .then (json = > dispatch (receivePosts (postTitle, json));}; / / method of use-store.dispatch (fetchPosts ('reactjs')); / / method II store.dispatch (fetchPosts (' reactjs')). Then () = > console.log (store.getState ()

In the above code, fetchPosts is an Action Creator (action generator) that returns a function. After this function is executed, an Action (requestPosts (postTitle)) is issued, followed by an asynchronous operation. After you get the result, first convert the result to JSON format, and then issue an Action (receivePosts (postTitle, json)).

There are several points to pay attention to in the above code.

(1) fetchPosts returns a function, while a normal Action Creator returns an object by default.

(2) the parameters of the returned function are the Redux methods dispatch and getState, and the parameters of ordinary Action Creator are the contents of Action.

(3) among the returned functions, issue an Action (requestPosts (postTitle)) to indicate the start of the operation.

(4) after the asynchronous operation ends, issue another Action (receivePosts (postTitle, json)) to indicate the end of the operation.

This process solves the problem of automatically sending a second Action. However, there is a new problem that the Action is sent by the store.dispatch method. Under normal circumstances, the store.dispatch method can only be an object, not a function.

At this point, the middleware redux-thunk should be used.

Import {createStore, applyMiddleware} from 'redux';import thunk from' redux-thunk';import reducer from'. / reducers';// Note: this API requires redux@ > = 3.1.0const store = createStore (reducer, applyMiddleware (thunk))

The above code uses redux-thunk middleware to modify store.dispatch so that the latter can accept a function as a parameter.

Therefore, the first solution for asynchronous operations is to write an Action Creator that returns the function, and then use redux-thunk middleware to transform the store.dispatch.

Back to the top (go to top)

The usage of React-Redux

For ease of use, the authors of Redux encapsulated a React-specific library, React-Redux, which is mainly introduced in this article.

This library is optional. In the actual project, you should weigh whether to use Redux directly or React-Redux. The latter provides convenience but requires additional API mastery and compliance with its component splitting specification.

React-Redux divides all components into two broad categories: UI components (presentational component) and container components (container component).

UI component

The UI component has the following characteristics.

Only responsible for the presentation of UI without any business logic

No state (that is, do not use the variable this.state)

All data is provided by the parameter (this.props)

API that does not use any Redux

Here is an example of a UI component.

Const Title = value = > {value}

Because it does not contain state, a UI component is also called a "pure component", that is, like a pure function, its value is determined purely by parameters.

Container component

The characteristics of container components are just the opposite.

Responsible for managing data and business logic, not responsible for UI presentation

With internal statu

API using Redux

In short, just remember one sentence: the UI component is responsible for the rendering of UI, and the container component is responsible for managing data and logic.

You might ask, what if a component has both UI and business logic? The answer is to split it into the following structure: on the outside is a container component with a UI component inside. The former is responsible for communicating with the outside, transmitting the data to the latter, and the latter renders the view.

React-Redux stipulates that all UI components are provided by the user, while container components are automatically generated by React-Redux. In other words, the user is responsible for the visual layer, and state management is all handed over to it.

Connect ()

React-Redux provides a connect method for generating container components from UI components. Connect means to connect the two components.

The full API of the connect method is as follows.

Import {connect} from 'react-redux'const VisibleTodoList = connect (mapStateToProps, mapDispatchToProps) (TodoList)

In the above code, TodoList is the UI component, and VisibleTodoList is the container component automatically generated by React-Redux through the connect method. The connect method accepts two parameters: mapStateToProps and mapDispatchToProps. They define the business logic of the UI component. The former is responsible for the input logic, that is, mapping the state to the parameters of the UI component (props), and the latter is responsible for the output logic, that is, mapping the user's operations on the UI component to Action.

MapStateToProps

MapStateToProps is a function. Its purpose is to establish, as its name suggests, a mapping from (external) state objects to (UI component's) props objects.

As a function, mapStateToProps should return an object after execution, where each key-value pair is a mapping. Take a look at the example below.

Const mapStateToProps = (state) = > {return {todos: getVisibleTodos (state.todos, state.visibilityFilter)}}

In the above code, mapStateToProps is a function that takes state as an argument and returns an object. This object has a todos property that represents the parameter of the same name for the UI component, and the following getVisibleTodos is also a function that calculates the value of todos from state.

Here is an example of getVisibleTodos, which is used to calculate todos.

Const getVisibleTodos = (todos, filter) = > {switch (filter) {case 'SHOW_ALL': return todos case' SHOW_COMPLETED': return todos.filter (t = > t.completed) case 'SHOW_ACTIVE': return todos.filter (t = >! t.completed) default: throw new Error (' Unknown filter:'+ filter)}}

MapStateToProps subscribes to Store and automatically executes whenever the state is updated, recalculating the parameters of the UI component, thus triggering the re-rendering of the UI component.

The first parameter of mapStateToProps is always the state object, and the second parameter can be used to represent the props object of the container component.

/ / Code of the container component / All// const mapStateToProps = (state, ownProps) = > {return {active: ownProps.filter = = state.visibilityFilter}}

After using ownProps as a parameter, if the parameters of the container component change, it will also cause the UI component to re-render.

The connect method can omit the mapStateToProps parameter, so that the UI component does not subscribe to Store, which means that the update of the Store does not cause the update of the UI component.

MapDispatchToProps ()

MapDispatchToProps is the second parameter of the connect function, which is used to establish the mapping of the parameters of the UI component to the store.dispatch method. That is, it defines which user's actions should be passed to Store as Action. It can be a function or an object.

If mapDispatchToProps is a function, you get two parameters, dispatch and ownProps (the props object of the container component).

Const mapDispatchToProps = (dispatch, ownProps) = > {return {onClick: () = > {dispatch ({type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter});};}

As you can see from the above code, mapDispatchToProps, as a function, should return an object where each key-value pair is a mapping that defines how the parameters of the UI component emit Action.

If mapDispatchToProps is an object, each key name is also a parameter of the corresponding UI component with the same name, the key value should be a function and will be treated as Action creator, and the returned Action will be automatically issued by Redux. For example, the mapDispatchToProps above is written as an object like this.

Const mapDispatchToProps = {onClick: (filter) = > {type: 'SET_VISIBILITY_FILTER', filter: filter};}

module

After the connect method generates the container component, you need to let the container component get the state object before you can generate the parameters of the UI component. React-Redux provides a Provider component that allows the container component to get the state.

Import {Provider} from 'react-redux'import {createStore} from' redux'import todoApp from'. / reducers'import App from'. / components/App'let store = createStore (todoApp); render (, document.getElementById ('root'))

In the above code, Provider wraps a layer outside the root component so that all subcomponents of App can get state by default.

React-Router routing Library

Projects that use React-Router are no different from other projects, and they also use Provider to make bread outside the Router. After all, the only function of Provider is to pass in store objects.

Const Root = ({store}) > ()

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report