What are the main problems solved by understanding Redux
React is the view layer framework. Redux is a JavaScript application for managing data state and UI state. With the increasing complexity of JavaScript single page application (SPA) development, JavaScript needs to manage more states than ever before, and Redux makes it easier to manage. Redux supports React, Angular, jQuery, and even pure JavaScript.
In React, the UI is built as components that can be nested and composed. However, the data flow between components in React is unidirectional. The top component can transfer data to the lower component through the props property, but the lower component cannot transfer data to the upper component, and the same cannot be done between sibling components. This simple one-way data flow underpins the controllability of data in React.
As the project gets larger, the number of events or callbacks that manage the data becomes more and more difficult to manage. Managing changing states is difficult. If a change in one model causes a change in another model, then a change in the View may cause a change in the corresponding model as well as in the other model, which in turn may cause a change in the other view. Until you can’t figure out what’s going on. When, for what reason, and how state changes are out of control. When systems become complex, it can be difficult to reproduce problems or add new features. If that’s not bad enough, consider some of the new requirements from the front-end development world, such as update tuning, server-side rendering, routing pre-jump request data, and so on. The management of state can be quite complex in large projects.
Redux provides a unified repository called the Store, where components pass state directly to the store via dispatches rather than through other components. And the component receives state changes from the store via subscribe. With Redux, all components can get the required state from the Store, and they can also get the change of state from the Store. This is much cleaner than passing data between components.
Redux is a state machine without UI rendering. Redux binds Redux’s state machine to react’S UI rendering. When you dispatch an action to change the state, it automatically updates the page.
Redux principle and workflow
(1) Principle Redux source code is mainly divided into the following several module files
- Compose. Js provides functional programming from right to left
- Createstore.js provides a function that generates a unique store
- CombineReducers. Js merges multiple reducer functions to ensure uniqueness of the store
- BindActionCreators. Js allows developers to change state without directly touching Dispacth
- Applymiddleware.js is a method that enhances dispatch with middleware
const actionTypes = { ADD: 'ADD', CHANGEINFO: 'CHANGEINFO', } const initState = { info: } export default function initReducer(state=initState, action) { switch(action.type) { case actionTypes.CHANGEINFO: return { ... state, info: action.preload.info || '', } default: return { ... state }; } } export default function createStore(reducer, initialState, middleFunc) { if (initialState && typeof initialState === 'function') { middleFunc = initialState; initialState = undefined; } let currentState = initialState; const listeners = []; If (middleFunc &&typeof middleFunc === 'function') {return middleFunc(reducer) initialState); } const getState = () => { return currentState; } const dispatch = (action) => { currentState = reducer(currentState, action); listeners.forEach(listener => { listener(); }) } const subscribe = (listener) => { listeners.push(listener); } return { getState, dispatch, subscribe } }Copy the code
(2) Workflow
- Const store= createStore (fn) Generates data;
- Action: {type: Symble(‘action01), payload:’payload’} Defines the action;
- Dispatch Initiate action: store.dispatch(doSomething(‘action001’));
- Reducer: Process the action and return a new state;
Popular point explanation:
- First, the user issues an Action (via the View) using the Dispatch method
- The Store then automatically calls the Reducer with two parameters: the current State and the received Action, and the Reducer returns the new State
- State – Once the View changes, the Store calls a listener to update the View
With Store as the core, it can be regarded as a data storage center, but it cannot directly modify the data when it wants to change the data. The role of data modification and update is assumed by Reducers, while Store only serves as storage and middleman. When the Reducers update is completed, the React Component will be notified through the store subscription, and the component will retrieve the rendering of the new state. The component can also actively send an action, which will not be executed after the action is created, so the action should be dispatched. Let the Store use reducers to update the React Component, which is each Component of React.
How are asynchronous requests handled in Redux
You can make requests directly in componentDidmount without resorting to Redux. However, in projects of a certain size, the above approach is difficult to manage asynchronous flows, and we usually use Redux’s asynchronous middleware for asynchronous processing. In fact, there are a lot of Redux asynchronous flow middleware, currently the mainstream asynchronous middleware has two kinds of Redux-Thunk, Redux-saga.
(1) Use the React – Thunk middleware
Story – thunk advantages:
- Small size: Redux-Thunk is simple to implement with less than 20 lines of code
- Simple to use: Redux-Thunk doesn’t introduce additional paradigms like Redux-Saga or Redux-Observable, making it easy to get started
Story – thunk defects:
- Too much boilerplate code: As with Redux itself, often a request requires a lot of code, much of it repetitive
- Serious coupling: Asynchronous operations are coupled with Redux’s actions, making it difficult to manage
- Functional weakness: Some of the features commonly used in real development need to be packaged themselves
Use steps:
- Configure the middleware, as in store creation
import {createStore, applyMiddleware, compose} from 'redux'; import reducer from './reducer'; Import thunk from 'redux-thunk' const composeEnhancers = window.__redux_devtools_extension_compose__? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose; Const enhancer = composeEnhancers(applyMiddleware(thunk)); const store = createStore(reducer, enhancer); export default store;Copy the code
- Add an actionCreator that returns a function to put asynchronous request logic in
/** Send the get request and generate the corresponding action, Update store function @param URL {string} request address @param func {function} really need to generate action corresponding actionCreator @return {function} */ / Export const getHttpAction = (url, Then (function(res){const action = func(res.data) dispatch(action)})} Copy codeCopy the code
- Generate the action and send the action
ComponentDidMount (){var Action = getHttpAction('/getData', getInitTodoItemAction); The action function automatically executes store.dispatch(action)}Copy the code
(2) Use redux-Saga middleware
Story – saga advantages:
- Asynchronous decoupling: Asynchronous operations are moved to separate saga.js, no longer doped in action.js or component.js
- Action gets rid of the Thunk Function: The dispatch argument is still a pure Action (FSA), not full of “dark magic” Thunk functions
- Exception handling: Thanks to the Saga implementation of Generator Function, code exceptions/request failures can be caught and handled directly using the try/catch syntax
- Powerful: Redux-Saga provides a large number of Saga helper functions and effects creators for developers to use without wrapping or simply wrapping
- Flexible: Redux-Saga can combine multiple sagas in serial/parallel form a very useful asynchronous flow
- Easy to test, provides a variety of case test solutions, including mock tasks, branch coverage, and so on
Story – saga defects:
- Additional learning costs: Redux-saga is not just using obscure generators Function, and there are dozens of apis. The learning cost is much higher than redux-Thunk. The most important thing is that your extra learning cost only serves this library, unlike Redux-Observable, which has extra learning cost but has RXJS and a whole set of thinking behind it Want to
- Large size: slightly large size, nearly 2000 lines of code, min version of about 25KB
- Feature overload: Concurrency control and other features are actually hard to use, but we still need to introduce this code
- Ts support is unfriendly: Yield cannot return ts
Redux-saga can capture the action and then execute a function, so you can put asynchronous code in this function, using the following steps:
- Configuring middleware
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga'
import TodoListSaga from './sagas'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const sagaMiddleware = createSagaMiddleware()
const enhancer = composeEnhancers(
applyMiddleware(sagaMiddleware)
);
const store = createStore(reducer, enhancer);
sagaMiddleware.run(TodoListSaga)
export default store;
Copy the code
- Place asynchronous requests in sagas.js
import {takeEvery, put} from 'redux-saga/effects' import {initTodoList} from './actionCreator' import {GET_INIT_ITEM} from './actionTypes' Import axios from 'axios' function* func(){try{// Can get asynchronous returned data const res = yield axios.get('/getData') const action = InitTodoList (res.data) // Send actions to reducer yield put(action)}catch(e){console.log(' network request failed ')}} function* mySaga(){ // Automatically capture GET_INIT_ITEM action and execute func yield takeEvery(GET_INIT_ITEM, func)} export default mySagaCopy the code
- Send the action
componentDidMount(){
const action = getInitTodoItemAction()
store.dispatch(action)
}
Copy the code
How does Redux implement attribute passing
React-redux data transmission: view–> Action –> Reducer –> Store –>view. Take a look at how the click event data is transferred to the view via redux:
- The AddClick event on the view passes data to action via mapDispatchToProps –> click:()=>dispatch(ADD)
- The ADD of the action was uploaded to the reducer
- < reducer > const store = createStore(reducer);
- Store map to the view using mapStateToProps text: state.text
Code examples:
import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux'; import { Provider, connect } from 'react-redux'; class App extends React.Component{ render(){ let { text, click, clickR } = this.props; Return (<div> <div> data: someone {text}</div> <div onClick={click}> adds </div> </div> </div>)}} const initialState = { text:5 } const reducer = function(state,action){ switch(action.type){ case 'ADD': return {text:state.text+1} case 'REMOVE': return {text:state.text-1} default: return initialState; } } let ADD = { type:'ADD' } let Remove = { type:'REMOVE' } const store = createStore(reducer); let mapStateToProps = function (state){ return{ text:state.text } } let mapDispatchToProps = function(dispatch){ return{ click:()=>dispatch(ADD), clickR:()=>dispatch(Remove) } } const App1 = connect(mapStateToProps,mapDispatchToProps)(App); ReactDOM.render( <Provider store = {store}> <App1></App1> </Provider>,document.getElementById('root') )Copy the code
What is Redux middleware? How many parameters are accepted? What are the parameters of the Currie function?
Redux’s middleware provides extension points after the action is initiated and before the reducer arrives. In other words, The data stream from View -> Action -> Reducer -> store became view -> Action -> Middleware -> Store In this section, you can do some “side effects” such as asynchronous requests, printing logs, and so on.
ApplyMiddleware source code:
export default function applyMiddleware(... middlewares) { return createStore => (... Const store = createStore(... args) => {const store = createStore(... args) let dispatch = () => { throw new Error() } const middlewareAPI = { getState: store.getState, dispatch: (... args) => dispatch(... Const chain = middlewares.map(Middleware => Middleware (middlewareAPI) // Compose then assemples all the anonymous functions in the chain into a new function, which is called dispatch = compose(... chain)(store.dispatch) return { ... Store, dispatch}}} copy codeCopy the code
From applyMiddleware:
- The Redux middleware takes an object as a parameter with two fields, Dispatch and getState, representing two functions of the same name on the Redux Store.
- The Two ends of the Currie function are middewares and Store. dispatch
How does Redux request middleware handle concurrency
Redux-saga is a middleware that manages asynchronous operations for Redux applications, in place of Redux-Thunk. It creates Sagas to store all asynchronous operation logic in one place for centralized processing, thus separating synchronous and asynchronous operations in React for later management and maintenance. How redux-Saga handles concurrency:
- takeEvery
Multiple Saga tasks can be forked in parallel.
import { fork, take } from "redux-saga/effects" const takeEvery = (pattern, saga, ... args) => fork(function*() { while (true) { const action = yield take(pattern) yield fork(saga, ... args.concat(action)) } })Copy the code
- takeLatest
TakeLatest does not allow multiple Saga tasks to be executed in parallel. Once the newly initiated action is received, it cancels all previously forked tasks (if they are still being executed). When dealing with AJAX requests, takeLatest is useful if you only want to get the response of the last request.
import { cancel, fork, take } from "redux-saga/effects" const takeLatest = (pattern, saga, ... args) => fork(function*() { let lastTask while (true) { const action = yield take(pattern) if (lastTask) { yield Cancel (lastTask) // Cancel is null if the task has already ended} lastTask = yield fork(saga,... args.concat(action)) } })Copy the code
What is the difference between a Redux state manager and a variable mounted in a Window
Both store data for later use. However, Redux state changes can be traced back — Time Travel, so when there is more data, you can clearly know where the change happened, providing a complete set of state management mode.
With the increasing complexity of JavaScript single-page application development, JavaScript needs to manage more states than ever before. These states may include server responses, cached data, locally generated data that has not yet been persisted to the server, as well as UI states such as active routes, selected labels, whether loading activity or pagers are displayed, and so on.
Managing changing states is difficult. If a change in one model causes a change in another model, then a change in the View may cause a change in the corresponding model as well as in the other model, which in turn may cause a change in the other view. Until you can’t figure out what’s going on. When, for what reason, and how state changes are out of control. When systems become complex, it can be difficult to reproduce problems or add new features. If that’s not bad enough, consider some of the new requirements from the front-end development world, such as update tuning, server-side rendering, routing pre-jump request data, and so on. Front-end developers are experiencing unprecedented complexity and just give up? Of course not.
Much of the complexity here comes from the fact that we often confuse two difficult concepts: change and asynchrony. Call them Mentos and Coke. If you separate them, you can do well, but when you mix them together, they get messy. Some libraries such as the React view address this problem by disabling asynchronous and direct DOM manipulation at the view layer. The fly in the ointment is that React still leaves it up to you to handle the data in state. Redux is there to help you solve that problem.
What’s the difference between Mobox and Redux?
(1) Common ground
- In order to solve the problem that status management is chaotic and cannot be synchronized effectively, unified maintenance and management of application status are maintained.
- A state has only one trusted data source (usually named store for state container);
- The mode of operation update status is unified and controllable (usually action mode provides the way of update status);
- Connect store to React components, such as react-redux and mobx-react.
(2) Redux is more of an implementation following the Flux mode. It is a JavaScript library with the following concerns
-
Action: a JavaScript object that describes information about an Action, including the type attribute and the payload attribute:
O type: action type; O payload: Payload data; Copy the codeCopy the code
-
Reducer: define how the application state responds to different actions and how to update the state;
-
Store: Manages actions and reducer and their relationships, providing the following functions
O Maintain application state and support access state (getState()); O Support to monitor the distribution of action, update status (dispatch(action)); O Support store changes (subscribe(listener)); Copy the codeCopy the code
-
Asynchronous flow: Since all Redux changes to store state should be triggered by action, asynchronous tasks (usually business or data acquisition tasks) are no exception. In order to avoid mixing business or data related tasks with React components, other frameworks need to be used to manage asynchronous task flows. Such as redux-thunk, redux-saga, etc.;
Mobx is a state management library for transparent functional responsive programming that makes state management simple and scalable
- Action: Defines the Action function for changing the state, including how to change the state;
- Store: State and Action of centralized management module
- Derivation: data that are derived from application states and have no other implications
Comparison summary:
- Redux stores data in a single store, while Mobx stores data in scattered stores
- Redux uses plain Objects to store data, requiring manual handling of changes. Mobx applies to an Observable that stores data and automatically processes the response when the data changes
- Redux uses immutable state, which means that the state is read-only and cannot be changed directly. Instead, a new state should be returned, using pure functions. State in MOBx is mutable and can be changed directly
- Mobx is relatively simple, in which there is a lot of abstraction, MOBx uses more object-oriented programming thinking; Redux can be complicated because the functional programming ideas are not so easy to master and require a range of middleware to handle asynchracy and side effects
- With more abstractions and encapsulation in Mobx, debugging can be difficult and the results unpredictable; Redux provides development tools that can do backtracking, while its pure functions and fewer abstractions make debugging much easier
What’s the difference between Redux and Vuex, their common idea
(1) Difference between Redux and Vuex
- Vuex improved the Action and Reducer functions in Redux, and replaced Reducer with the mutations change function, without switch, only need to change the state value in the corresponding mutation function
- Vuex doesn’t need to subscribe to the rerender function because of Vue’s automatic rerender feature, just generate a new State
- The sequence of Vuex data stream is as follows: View calls store.mit to submit the corresponding request to the corresponding mutation function in Store -> Store change (vUE automatically renders when it detects the data change).
Commonly understood as vuex weakened dispatch, a change of store state through commit; Cancelling the action concept without passing in a specific action form to specify changes; Weaken reducer, directly transform data based on commit parameters, making the framework more simple;
(2) Common ideas
- Single – data source
- Change is predictable
In essence: Both Redux and VUex are services to the MVVM idea, a solution to pull data out of the view.
How does Redux middleware get store and Action? And then what do we do?
Redux middleware is essentially a function cremation. The source code for the Redux applyMiddleware Api takes two arguments each, a Store getState function and a dispatch function, which get Store and action, respectively, and return a function. This function is passed to Next’s next Middleware’s Dispatch method and returns a new function that receives an action, which can call Next (Action) directly, at any other time needed, or not at all. The last middleware in the call chain accepts the actual Store’s dispatch method as the next argument and ends the call chain. So, the middleware function signature is ({getState, dispatch})=> next => action.
What does Connect do in Redux
Connect connects React and Redux
(1) Obtain state
Connect uses context to get stores in the Provider, and uses store.getState() to get all states in the entire Store tree
(2) Packaging the original components
Pass state and action inside the component as props. WrapWithConnect returns a ReactComponent object, Connect, Connect rerender the WrappedComponent and merge the mapStateToProps and mapDispatchToProps passed in Connect with the props of the component. Pass to WrappedComponent as a property
(3) Monitor store tree changes
Connect caches the state of the state in the Store Tree and compares the current state with the state before the change to determine whether the this.setState() method is called to trigger the re-rendering of connect and its children