This section

Classroom goal

redux

resources

redux

react-redux

start

Redux is quick to get started

1. Install

npm i redux -S
Copy the code

2. Characters in Redux

  • Store
    • Maintain application state;
    • Provide the getState() method to getState;
    • Provide a dispatch(action) method to update state;
    • Register a listener with subscribe(listener);
    • Unsubscribe the listener via a function returned by subscribe(listener).
  • Reducer: Specifies how application state changes are sent to the store in response to actions
  • Action: The payload for transferring data from the app to the Store

store.js

import {
    createStore
} from 'redux';
Create a Reducer state modifier
function counter(state = 0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DRCREMENT':
            return state - 1;
        default:
            returnstate; }}// Create store and export
export default createStore(counter);
Copy the code

ReduxTest.js

import React, { Component } from 'react';
import store from '.. /store';

class ReduxTest extends Component {
    render() {
        return (
            <div>
                <p>
                    {store.getState()}
                </p>
                <button onClick={()= > store.dispatch({ type:"DRCREMENT"})}>-1</button>
                <button onClick={()= > store.dispatch({ type: "INCREMENT" })}>+1</button>
            </div>); }}export default ReduxTest;
Copy the code

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import ReduxTest from './components/ReduxTest';
import store from './store'
function render() {
    ReactDOM.render(<ReduxTest />.document.querySelector('#root'));
}
render();
// Logs are printed each time state is updated
// Notice that subscribe() returns a function to unsubscribe listeners
/ / subscribe
store.subscribe(render)
Copy the code

At the heart of the Redux architecture’s design: strict one-way data flow

Problem: Every state update rerenders, causing unnecessary duplication in large applications.

How to use Redux more gracefully? react-redux

react-redux

npm i react-redux -S
Copy the code

Specific steps:

  • React Redux provides that<Provider />To make the Redux Store available to your application

Modified index. Js

import React from 'react';
import ReactDOM from 'react-dom';
import ReduxTest from './components/ReduxTest';
import store from './store'
import { Provider } from 'react-redux';
function render() {
    ReactDOM.render((
        <Provider store = {store}>
            <ReduxTest />
        </Provider>
    ), document.querySelector('#root'));
}
render();
// Subscriptions are no longer needed
// store.subscribe(render);
Copy the code

React Redux provides connect to connect components to stores

Modify ReduxTest. Js

import React, { Component } from 'react';
import { connect } from "react-redux";
const mapStateToProps = state= > {
    return {
        num: state
    }
}
const mapDispatchToProps = dispatch= > {
    return {
        increment: () = > {
            dispatch({ type: 'INCREMENT'})},decrement: () = > {
            dispatch({
                type: 'DRCREMENT'})}}}class ReduxTest extends Component {
    render() {
        return (
            <div>
                <p>{this.props.num}</p>
                <button onClick={()= > this.props.decrement()}>-1</button>
                <button onClick={()= > this.props.increment()}>+1</button>
            </div>); }}export default connect(mapStateToProps, mapDispatchToProps)(ReduxTest);;
Copy the code

Decorator writing

mport React, { Component } from 'react';
import { connect } from "react-redux";
const mapStateToProps = state= > {
    return {
        num: state
    }
}
const mapDispatchToProps = dispatch= > {
    return {
        increment: () = > {
            dispatch({ type: 'INCREMENT'})},decrement: () = > {
            dispatch({
                type: 'DRCREMENT'
            })
        }
    }
}
@connect(mapStateToProps, mapDispatchToProps)
class ReduxTest extends Component {
    render() {
        return (
            <div>
                <p>{this.props.num}</p>
                <button onClick={()= > this.props.decrement()}>-1</button>
                <button onClick={()= > this.props.increment()}>+1</button>
            </div>); }}export default ReduxTest;
Copy the code

The container component reads some data from the Redux state tree using store.subscribe() and feeds that data to the component to be rendered using props. You can develop container components manually, but it is recommended to use the Connect () method of the React Redux library, which is optimized for performance to avoid many unnecessary repeated renderings.

Before using connect(), you need to define the function mapStateToProps to specify how to map the current Redux store state to the props of the presentation component.

Redux middleware

The Redux middleware mechanism allows additional business logic to be executed prior to the actual action response.

Features: free combination, free plug – in mechanism

There is usually no need to write our own middleware. Here are two more mature middleware

  • Redux-logger: Middleware that handles logging
  • Redux-thunk: handles asynchronous actions
npm i redux-thunk redux-logger -S
Copy the code

The use of redux-Logger is added in store.js

import {
    createStore,
    applyMiddleware
} from 'redux';
import logger from 'redux-logger';
/ / create a reducer
function counter(state = 0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DRCREMENT':
            return state - 1;
        default:
            returnstate; }}export default createStore(counter, applyMiddleware(logger));
Copy the code

Effect:

Redux-thunk is modified in store.js

import {
    createStore,
    applyMiddleware
} from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
/ / create a reducer
function counter(state = 0, action) {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DRCREMENT':
            return state - 1;
        default:
            returnstate; }}export default createStore(counter, applyMiddleware(logger,thunk));
Copy the code

Add thunk: Action takes an object by default and executes the next task, or asynchronously if it’s a function.

Redux-thunk is modified in reduxtest.js

import React, { Component } from 'react';
// import store from '.. /store';
import { connect } from "react-redux";
const mapStateToProps = state= > {
    return {
        num: state
    }
}
const asyncAdd = () = > {
    return (dispatch,getState) = >{
        setTimeout(() = > {
            dispatch({type:'INCREMENT'})},1000); }}const mapDispatchToProps = (dispatch) = > {
    return {
        increment: () = > {
            dispatch({ type: 'INCREMENT' });
        },
        decrement: () = > {
            dispatch({
                type: 'DRCREMENT'})},asyncIncrement: () = > {
            // The default action of an action is an object. If it is a return function, redux-thunk is used
            dispatch(asyncAdd());
        }

    }
}
@connect(mapStateToProps, mapDispatchToProps)
class ReduxTest extends Component {
    render() {
        return (
            <div>
                <p>{this.props.num}</p>
                <button onClick={()= > this.props.decrement()}>-1</button>
                <button onClick={()= > this.props.increment()}>+1</button>
                <button onClick={()= > this.props.asyncIncrement()}>async+1</button>
            </div>); }}export default ReduxTest;
Copy the code

Effect display:

Reconstruction project

The new store/couter. Reduce. Js

/ / create a reducer
const counter = (state = 0, action) = > {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DRCREMENT':
            return state - 1;
        default:
            returnstate; }}export const mapStateToProps = state= > {
    return {
        num: state
    }
}
const asyncAdd = () = > {
    return (dispatch, getState) = > {
        setTimeout(() = > {
            dispatch({ type: 'INCREMENT'})},1000); }}export const mapDispatchToProps = (dispatch) = > {
    return {
        increment: () = > {
            // dispatch({ type: 'INCREMENT' })
            dispatch({ type: 'INCREMENT' });
        },
        decrement: () = > {
            dispatch({
                type: 'DRCREMENT'})},asyncIncrement: () = >{ dispatch(asyncAdd()); }}}export default counter;
Copy the code

The new store/index. Js

import {
    createStore,
    applyMiddleware
} from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import counter from './couter.reducer';

export default createStore(counter, applyMiddleware(logger,thunk));
Copy the code

Refactoring ReduxTest. Js

import React, { Component } from 'react';
// import store from '.. /store';
import { connect } from "react-redux";
import {mapStateToProps,mapDispatchToProps} from '.. /store/couter.reducer';

@connect(mapStateToProps, mapDispatchToProps)
class ReduxTest extends Component {
    render() {
        return (
            <div>
                <p>{this.props.num}</p>
                <button onClick={()= > this.props.decrement()}>-1</button>
                <button onClick={()= > this.props.increment()}>+1</button>
                <button onClick={()= > this.props.asyncIncrement()}>async+1</button>
            </div>); }}export default ReduxTest;
Copy the code

Merger of reducer

CombineReducers are used to compound and achieve the modularization of the state

import {
    createStore,
    applyMiddleware,
    combineReducers
} from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import counter from './couter.reducer';

export default createStore(
    combineReducers({
        counter
    }), applyMiddleware(logger,thunk));
Copy the code

counter.reducer.js

export const mapStateToProps = state= > {
    return {
       // Add the current state key
        num: state.counter
    }
}
Copy the code

Mobx Quick start

React and MobX are a powerful duo.

React is a consumer that renders application state as a tree of components.

Mobx is a provider for storing and updating state states

download

npm i mobx mobx-react -S
Copy the code

The new store/mobx. Js

import { observable,action,computed} from "mobx";

/ / observer
const appState = observable({
    num: 0
})
/ / method
appState.increment = action(() = >{
    appState.num+=1;
})
appState.decrement = action(() = >{
    appState.num-=1;
})
export default appState;
Copy the code

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import MobxTest from "./components/MobxTest";
ReactDOM.render((
    <div>
        <MobxTest appState = {appState}/>
    </div>
), document.querySelector('#root'));
Copy the code

MobxTest.js

import React, { Component } from 'react';
import { observer } from "mobx-react";
class MobxTest extends Component {
    render() {
        return (
            <div>
                {this.props.appState.num}
                <button onClick={()= > this.props.appState.decrement()}>-1</button>
                <button onClick={()= > this.props.appState.increment()}>+1</button>
            </div>); }}export default observer(MobxTest);
Copy the code

Decoration written:

store/mobx.decorator.js

import { observable, action, computed } from "mobx";
// Change constant to class
class AppState {
    @observable num = 0;
    @action
    increment(){
        this.num +=1;
    }
    @action
    decrement() {
        this.num -= 1; }}const appState = new AppState();

export default appState;
Copy the code

MobxTest.decorator.js

import React, { Component } from 'react';
import { observer } from "mobx-react";
@observer
class MobxTest extends Component {
    render() {
        return (
            <div>
                {this.props.appState.num}
                <button onClick={()= > this.props.appState.decrement()}>-1</button>
                <button onClick={()= > this.props.appState.increment()}>+1</button>
            </div>); }}export default MobxTest;
Copy the code

Compare React and Mobx

  • Learning difficulty
  • The workload
  • Memory overhead
  • Centralization of state management
  • The need for boilerplate code
  • Bottom line: Mobx is easy to get started and quick to build, but when the project is big enough, redux is still a must-have, which opens up strict mode with a set of state management specifications. A p

The react – router4.0

resources

react-router

Quick start

The installation

npm install react-router-dom --save
Copy the code

Basic Routing usage

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
function Home() {
    return (
        <h2>I am a home page</h2>)}function Course() {
    return (
        <h2>I am a course</h2>)}function User() {
    return (
        <h2>I am a home page</h2>)}class Basic_router extends Component {
    render() {
        return (
            <Router>
                <div>{/* Define routing page */}<ul>
                        <li>
                            <Link to='/'>Home page</Link>
                        </li>
                        <li>
                            <Link to='/course'>course</Link>
                        </li>
                        <li>
                            <Link to='/user'>The user</Link>
                        </li>
                    </ul>{/* Specify route */} {/* specify exact route */} {/* Specify exact route */}<Route exact path='/' component={Home}></Route>
                    <Route path='/course' component={Course}></Route>
                    <Route path='/user' component={User}></Route>
                </div>

            </Router>); }}export default Basic_router;
Copy the code

Route URL parameter(Level-2 route)

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
function Home() {
    return (
        <h2>I am a home page</h2>)}function Course() {
    return (
        <div className='course'>
            <h2>I of course</h2>{/* Define the secondary routing page */}<ul>
                <li>
                    <Link to='/course/vue'>Vue</Link>
                </li>
                <li>
                    <Link to='/course/React'>React</Link>
                </li>
                <li>
                    <Link to='/course/Angular'>Angular</Link>
                </li>
            </ul>{/* Configure route parameters */}<Route path='/course/:id' component={CourseChild}></Route>
            <Route exact path={match.path} render={()= > <h3>Please choose your course</h3>} / ></div>)}function User() {
    return (
        <h2>I am a home page</h2>)}// The secondary routing page is displayed
function CourseChild({match,history,location}) {
    //match: matches the routing information object
    //location: local information object
    //history: historical information object
    console.log(location,match,history);
    
    return (
        <div>
            {match.params.id}
        </div>)}class Basic_router extends Component {
    render() {
        return (
            <Router>
                <div>{/* Define routing page */}<ul>
                        <li>
                            <Link to='/'>Home page</Link>
                        </li>
                        <li>
                            <Link to='/course'>course</Link>
                        </li>
                        <li>
                            <Link to='/user'>The user</Link>
                        </li>
                    </ul>{/* Configure the route */}<Route exact path='/' component={Home}></Route>
                    <Route path='/course' component={Course}></Route>
                    <Route path='/user' component={User}></Route>
                   
                </div>

            </Router>); }}export default Basic_router;
Copy the code

The Course component above can also be modified in this way

function Course({match}) {
    return (
        <div className='course'>
            <h2>I of course</h2>
            <ul>
                <li>
                    <Link to={` ${match.url} /vue`} >Vue</Link>
                </li>
                <li>
                    <Link to={` ${match.url} /react`} >React</Link>
                </li>
                <li>
                    <Link to={` ${match.url} /angular`} >Angular</Link>
                </li>
            </ul>
            <Route path='/course/:id' component={CourseChild}></Route>
            <Route exact path={match.path} render={()= > <h3>Please choose your course</h3>} / ></div>)}Copy the code

Don’t match (404).

// 404 page display
function NoMatch() {
    return <div>404 page, page lost</div>
}
class Basic_router extends Component {
    render() {
        return (
            <Router>
                <div>{/* Define routing page */}<ul>
                        <li>
                            <Link to='/'>Home page</Link>
                        </li>
                        <li>
                            <Link to='/course'>course</Link>
                        </li>
                        <li>
                            <Link to='/user'>The user</Link>
                        </li>
                    </ul>{/* Configure the route */}<Route exact path='/' component={Home}></Route>
                    <Route path='/course' component={Course}></Route>
                    <Route path='/user' component={User}></Route>{/* Add mismatched route configuration */}<Route  component={NoMatch}></Route>
                </div>

            </Router>); }}Copy the code

At this point, you’ll find that each page matches the NoMatch component, and it’s time for the Switch component to ship

Modify the above code as follows

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link,Switch } from "react-router-dom";
// 404 page display
function NoMatch() {
    return <div>404 page, page lost</div>
}
class Basic_router extends Component {
    render() {
        return (
            <Router>
                <div>{/* Define routing page */}<ul>
                        <li>
                            <Link to='/'>Home page</Link>
                        </li>
                        <li>
                            <Link to='/course'>course</Link>
                        </li>
                        <li>
                            <Link to='/user'>The user</Link>
                        </li>
                    </ul>{/* Configure the route */}<Switch>
                        <Route exact path='/' component={Home}></Route>
                        <Route path='/course' component={Course}></Route>
                        <Route path='/user' component={User}></Route>{/* Add mismatched route configuration */}<Route component={NoMatch}></Route>
                    </Switch>
                </div>

            </Router>); }}Copy the code

Imperative navigation

function Home({ location }) {
    console.log(location);
    return (
        <div>
            <h1>{location.state ? location.state.foo : ""}</h1>
            <h2>I am a home page</h2>
        </div>)}function CourseChild({ match, history, location }) {
    return (
        <div>} {match. Params. Id<button onClick={history.goBack}>return</button>
            <button onClick={()= >{history.push('/')}}> Jump to the first page</button>
            <button onClick={()= >{history. Push ({pathname: '/', state: {foo: 'bar'}})}} > jump front page, and they carry a value</button>
        </div>)}Copy the code

Redirect Redirect

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link, Switch, Redirect} from "react-router-dom";

function UserDeatil({match,location}) {
    return (
        <div>Personal Details Page</div>)}function UserOrder(params) {
    return (
        <div>User order page</div>)}function User() {
    return (
       <div>
           <h2>
               <Link to='/user/detail'>Personal information</Link>
           </h2>
            <h2>
                <Link to='/user/order'>Individual orders</Link>
            </h2>
            <Switch>
                <Route path="/user/detail" component={UserDeatil}></Route>
                <Route path="/user/order" component={UserOrder}></Route>{/* Redirection */}<Redirect to='/user/detail'></Redirect>
            </Switch>
       </div>)}class Basic_router extends Component {
    render() {
        return (
            <Router>
                <div>{/* Define routing page */}<ul>
                        <li>
                            <Link to='/user'>The user</Link>
                        </li>
                    </ul>{/* Configure the route */}<Switch>
                        <Route path='/user' component={User}></Route>
                    </Switch>
                </div>

            </Router>); }}export default Basic_router;
Copy the code

Routing guard

Define higher-order components that can be verified

// Route guard: defines a higher-order component that can be verified
function PrivateRoute({ component: Component, ... rest }) {
    return (
        <Route
            {. rest}
            render={props= >
                Auth.isAuth ? (
                    <Component {. props} / >
                ) : (
                        <Redirect to={{
                            pathname: "/login",
                            state: { from: props.location }
                        }}
                        />
                    )
            }
        />)}Copy the code

Certification class Auth

const Auth = {
    isAuth: false.login(cb) {
        this.isAuth = true;
        setTimeout(cb, 1000);
    },
    signout(cb) {
        this.isAuth = false;
        setTimeout(cb, 1000); }}Copy the code

Defining login components

class Login extends Component {
    state = { isLogin: false };
    handlerlogin = () = > {
        Auth.login(() = > {
            this.setState({
                isLogin:true})})}render() {
        let { isLogin } = this.state;
        let { from } = this.props.location.state || { from: { pathname: '/'}}if (isLogin) return <Redirect to={from} />
        return (
            <div>
                <p>Please login first</p>
                <button onClick={this.handlerlogin}>The login</button>
            </div>); }}Copy the code

Custom routes and custom login route configurations are used in the primary routing component

<Switch>
    <Route exact path='/' component={Home}></Route>
    {/* <Route path='/course' component={Course}></Route> */}
    <PrivateRoute path='/course' component={Course}></PrivateRoute>
    <Route path='/user' component={User}></Route>
    <Route path='/login' component={Login}></Route>
    <Route component={NoMatch}></Route>
</Switch>
Copy the code

Integrate into REdux

  1. New/store/user. Reducer. Js

    const initState = {
      isLogin: false.// Indicates that the user is not logged in
      userInfo: {}}function user(state = initState, action) {
      switch (action.type) {
        case 'login':
          return { isLogin: true }
    
        default:
          return initState
      }
    
    }
    
    export const mapStateToProps = state= > {
      return {
        // Add the current state key for modular identification
        user: state.user
      }
    }
    
    const login = () = > {
      return (dispatch) = > {
        setTimeout(() = > {
          dispatch({ type: 'login'})},1000); }}export const mapDispatchToProps = dispatch= > {
      return {
        // Action receives an object by default and executes the next task. If it is a function, it needs to be handled asynchronously, react-thunk
        login: () = > {
          dispatch(login())
        }
      }
    }
    export default user
    Copy the code

    The new store/index. Js

    
    // combineReducers can be combined to achieve the modularization of the status
    import { createStore, applyMiddleware, combineReducers } from "redux";
    import logger from "redux-logger";
    import thunk from "redux-thunk";
    import user from './user.reducer'
    
    // Create store There are state and Reducer stores
    const store = createStore(combineReducers({
      user
    }), applyMiddleware(logger, thunk));
    export default store;
    Copy the code

    In the index. Js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import { Provider } from "react-redux";
    import store from './store/index'
    ReactDOM.render((
      <Provider store={store}>
        <App />
      </Provider>
    ), document.getElementById('root'));
    
    
    Copy the code

    App. Js modified

    .import { connect } from "react-redux";
    import { mapStateToProps } from "./store/user.reducer";
    // Higher-order component: the routing component that defines the validation function
    @connect(mapStateToProps)
    class PrivateRoute extends Component {
      render() {
        const Comp = this.props.component;
        return (
          <Route
            {. this.props}
            component={
              (props) = >
                this.props.user.isLogin ?
                  (<Comp {. props} / >) :
                  (<Redirect to={{ pathname: '/login', state: { from: props.location } }} />)
            }>
          </Route>)}}...Copy the code

    The login.js component is modified

    import React, { Component } from 'react'
    import Auth from '.. /utils/auth';
    import { Button } from "antd";
    import { Redirect } from "react-router-dom";
    import { connect } from "react-redux";
    import { mapStateToProps, mapDispatchToProps } from '.. /store/user.reducer';
    @connect(mapStateToProps,mapDispatchToProps)
     class Login extends Component {
      handleLogin = () = > {
        // Asynchronous processing
        this.props.login();
      }
    
      render() {
        let { isLogin } = this.props.user;
        let path = this.props.location.state.from.pathname
        if (isLogin) {
          return <Redirect to={path}/>
    
        } else {
          return (
            <div>
              <p>Please login first</p>
              <Button onClick={this.handleLogin}>The login</Button>
            </div>)}}}export default Login
    Copy the code

Redux principle

  • createStoreIs a function that takes three argumentsreducer,preloadedState,enhancer
    • enhancerIs a higher-order function that enhances the store created by create. Its argument iscreateStore, returns a more powerful store generator. (Functions like middleware).
    • In our mobile warehousestoreCreatorThis is actually an enhancer, adding saga to the createStore instead of as the third parameter in the createStoremiddlewareTo complete.
export default function createStore(reducer,preloadedState,enchancer) {
    
	 if (typeof preloadedState === 'function' && typeof enhancer === 'function' || typeof enhancer === 'function' && typeof arguments[3= = ='function') {
        throw new Error('It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function.');
    }
	// If the second argument preloadedState is passed, and the second argument is not a function, then preloadedState is stored in the internal variable currentState, which is the default State we gave State
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
        enhancer = preloadedState;
        preloadedState = undefined;
    }

    if (typeofenhancer ! = ='undefined') {
        if (typeofenhancer ! = ='function') {
            throw new Error('Expected the enhancer to be a function.');
        }
        // createStore as enhancer, return a strengthened createStore, and then pass the reducer and preloadedState into the store
        return enhancer(createStore)(reducer, preloadedState);
    }
	The first reducer parameter must be passed and must be a function, otherwise Redux will report an error
    if (typeofreducer ! = ='function') {
        throw new Error('Expected the reducer to be a function.');
    }
	// A state tree is stored inside the warehouse. It can be any type
	let currentState = preloadedState;
	let currentListeners=[];
    let currentReducer = reducer
	function getState() {
		return JSON.parse(JSON.stringify(state));
	}
	// The component can dispatch actions to the warehouse
	function dispatch(action) {
		// Call reducer for processing, obtain the old state, and calculate the new state
		currentState=currentReducer(currentState,action);
		// Tell other components to execute
		currentListeners.forEach(l= >l());
	}
	// If other components need to subscribe to state change time,
	function subscribe(listener) {
        // Put the listener into a queue
		currentListeners.push(listener);
		return function () {
			currentListeners = currentListeners.filter(item= >item!==listener);
		}
	}
    // Initialize the operation
	dispatch({type:'@@INIT'});
	return {
		getState,
		dispatch,
		subscribe
	}
}
Copy the code
  • Relationship between applyMiddleware and enhancer
    • First of all, they both have the same function, which is to enhance the Store
    • ApplyMiddleware results in an enhancer
export function applyMiddleware(. middlewares){
    return  (createStore) = > {
        return function (. args) {
            // Create the original store
            conststore = createStore(... args);// Get the original dispatch
            const _dispatch = store.dispatch;
            const middlewareAPI = {
                getState: store.getState,
                dispatch: (. args) = > {
                    returndispatch(... args) } };// Invoke tier 1 middleware
            const middlewareChain = middlewares.map( (middleware) = > {
                // Let each middleware execute, passing in an object {getState,dispatch}
                return middleware(middlewareAPI);
            });
            // With the compose composite function, the current middleware is done and the next middleware is called and the value (store.dispatch) is passed in to enhance the dispatch_dispatch = compose(... middlewareChain)(store.dispatch);// return a store that has been enhanced with dispatch
            return  {
                ...store,
                dispatch: _dispatch
             };
        };
    };
}
function compose(. fns){ //[add1,add2,add3] are functions
    if(fns.length === 0) {return arg= > arg;
    }
    if(fn2.length === 1) {return fns[0]}return fns.reduce((f1,f2) = >(. args) = >f1(f2(... args))) }Copy the code

The React – redux principle

import React,{Component} from 'react';
import {bindActionCreators} from '.. /redux';
MapStateToProps is a function that maps state to a property object. MapDispatchToProps is a function that maps the dispatch method to a property object
export default function connect(mapStateToProps,mapDispatchToProps) {
    return function (Com) {
        // Implement the repository and component connection in this component
        class Proxy extends Component{
        state=mapStateToProps(this.props.store.getState())
        componentDidMount() {
            // Update the status
            this.unsubscribe = this.props.store.subscribe(() = > {
             this.setState(mapStateToProps(this.props.store.getState()));
            });
        }
        componentWillUnmount = () = > {
            this.unsubscribe();
        }
        render() {
            let actions={};
            // If mapDispatchToProps is a function, it gets the property object
            if (typeof mapDispatchToProps === 'function') {
                actions = mapDispatchToProps(this.props.store.dispatch);
                // If mapDispatchToProps is an object, we need to bind it manually
            } else {
             actions=bindActionCreators(mapDispatchToProps,this.props.store.dispatch);
            }
            return <Com {. this.state} {. actions} / >}}}export default class Provider extends Component{
	// specifies that if someone wants to use this component, they must provide a redux repository property
	static propTypes={
		store:PropTypes.object.isRequired
	}
	render() {
		let value={store:this.props.store};
		return (
			<StoreProvider value={value}>
				{this.props.children}
			</StoreProvider>)}}Copy the code

redux-thunk

const thunk = ({dispatch,getState}) = >next= >action= >{
    if(typeof action=='function') {return action(dispatch,getState)
    }
    return next(action)
}
export default thunk;
Copy the code

Perfect solution for Redux-Saga

Redux-saga is a library for managing Side effects (Side effects, such as asynchronously fetching data, accessing browser caches, etc.) in applications, with the goal of making Side effects management easier, execution more efficient, testing simpler, and handling failures easier.

Redux-saga uses ES6’s Generator functionality to make asynchronous processes easier to read, write, and test.

In this way, these asynchronous processes look like standard synchronous Javascript code.

Unlike Redux Thunk, you don’t have callback hell anymore, and you can easily test asynchronous flows and keep your actions clean.

The installation

npm install redux-saga --save
Copy the code

The new store/sagas. Js

import { call, put, takeEvery } from "redux-saga/effects";

// The API for simulating login is usually placed in the service folder during project development
const api = {
    login(){
        return new Promise((resolve, reject) = > {
            setTimeout(() = > {
                if(Math.random() > 0.5){
                    resolve({id:1.name:"Tom"})}else{
                    reject('Wrong username or password')}},1000); }}})// Worker saga: the login action will be invoked when dispacth
function* login(action) {
    try {
        const result = yield call(api.login);
        yield put({ type: 'login', result });
    } catch (error) {
        yield put({ type: 'loginError'.message: error.message }); }}// Similar to listener
function* mySaga() {
    yield takeEvery('login_request',login);
}
export default mySaga;
Copy the code

To run Saga, we need to use the Redux-Saga middleware to connect Saga to the Redux Store.

Modify the store/index. Js

import {
    createStore,
    applyMiddleware,
    combineReducers
} from 'redux';
import logger from 'redux-logger';
/ / register reducer
import user from './user.reducer';
import createSagaMiddleware from 'redux-saga'
import mySaga from './sagas';

1. Create middleware
const mid = createSagaMiddleware();
// createSagaMiddleware is a factory function that creates a SagaMiddleware after passing the helloSaga argument
// Use applyMiddleware to connect the middleware to store
//2. Application middleware
const store =  createStore(
    combineReducers({
        user
    })
    , applyMiddleware(logger,mid));
Run the middleware
mid.run(mySaga)
export default store;
Copy the code

Modify user. Reducer. Js

// Define the reducer of the user
const initialState = {
    isLogin: false.// No login at first
}

export default (state = initialState, { type, payload }) => {
    switch (type) {
        case 'login':
            // return Object.assign({}, state, {
            // isLogin: true
            // })
            return{... state, ... {isLogin: true}};// return {isLogin:true}
        default:
            return state
    }
}

export const mapStateToProps = state= > {
    const {isLogin} = state.user;
    return {
        isLogin: isLogin
    }
}
export const mapDispatchToProps = (dispatch) = > {
    return {
        login: () = >{ dispatch(asyncLogin()); }}}// The asynchronous method for redux-thunk
/* function asyncLogin() { return (dispatch) => { setTimeout(() => { dispatch({ type: 'login' }) }, 1250); }} * /

// for redux-saga
function asyncLogin() {
    alert(1);
    return {type:'login_request'}}Copy the code

Difference between redux-Thunk and Redux-saga

Thunk is acceptablefunctionThe type ofaction.sagaAre pure objectsactionThe solutionsagausegeneratorTo solve asynchronous problems, it is very easy to write asynchronous code synchronouslyCopy the code

UmiJS

It is a pluggable enterprise react application framework. Umi is based on routing and equipped with a complete plug-in system. It covers every life cycle from source code to build product, supports various functional extensions and business requirements, and currently has more than 50 internal and external plug-ins.

Umi is the bottom front-end framework of Ant Financial, which has directly or indirectly served over 600 applications, including Java, Node, H5 wireless, Hybrid applications, pure front-end assets applications, CMS applications, etc. He has served our internal users well, and hopefully he will serve our external users well.

features

  • 📦 Built-in react, react-Router, and so on out of the box
  • 🏈 class next. Js and fully functional routing convention, and support the configured routing mode
  • 🎉 complete plug-in system, covering every life cycle from source code to build product
  • 🚀 High performance, support PWA and code Splitting with routing as a unit through plug-ins, etc
  • 💈 supports static page export, suitable for various environments, such as ZHONGtai business, wireless business, Egg, Alipay wallet, Cloud Fengdee, etc
  • 🚄 development start fast, support a key to open DLL
  • 🐠 one-click compatibility with IE9, based on UMi-plugin-polyfills
  • 🍁 Full TypeScript support, including d.ts definitions and umi test
  • 🌴 is deeply integrated with DVA data stream, supporting Duck Directory, automatic loading of model, code Splitting, etc

Quick learning

NPM I YARN Tyarn -g # Change all future yarn to Tyarn download # Global Install UMI, ensure the version is2.0. 0The above yarnglobal add umi
Copy the code

The scaffold

Find an empty place to create an empty directory

mkdir umi_app && cd umi_app
Copy the code

Then create some pages using Umi G

umi g page index
Copy the code

Run the tree command to view the directory structure

├── Pages ├─ index.css ├─ index.jsCopy the code

Then start the local server

umi dev
Copy the code

In-page jump

Documentation reference, so easy

routing

Umi automatically generates route configurations based on the Pages directory

Based on the routing

This operation is demonstrated above

Dynamic routing

According to umi, directories or files with $prefix are dynamic routes

The directory structure is as follows:

└ ─ ─ pages ├ ─ ─ index. CSS ├ ─ ─ index. The js └ ─ ─ the users ├ ─ ─ $id. The CSS ├ ─ ─ $id. JsCopy the code

The route configuration is as follows:

{
    path: '/users/:id'.exact: true.component: require('.. /users/$id.js').default,
}
Copy the code

Modify the $id. Js


// Approximate routing
import styles from './$id.css';

export default function ({match}) {
    return (
        <div className={styles.normal}>
            <h1>user index {match.params.id}</h1>
        </div>
    );
}

Copy the code

To see the effect, visit localhost:8000/users/1 and localhost:8000/user/2

Embedded routines by

Umi convention generates nested routines when _layout.js exists in the directory. _layout.js is the layout of the directory

umi g users/_layout
umi g users/index
Copy the code

Generate the following directory structure

└ ─ ─ pages ├ ─ ─ index. CSS ├ ─ ─ index. The js └ ─ ─ the users ├ ─ ─ $id. The CSS ├ ─ ─ $id. Js ├ ─ ─ _layout. CSS ├ ─ ─ _layout. JsCopy the code

The route configuration is as follows:

{
    path: '/users',
    exact: false, component: require('.. /users/_layout.js').default, routes: [ { path: '/users', exact:true, component: require('.. /users/index.js').default, }, { path: '/users/:id', exact:true, component: require('.. /users/$id.js').default, }, ] }Copy the code

users/_layout.js


import styles from './_layout.css';

export default function(props) {  
  return (
    <div className={styles.normal}>
      <h1>Page _layout</h1>
      <div>
        {props.children}
      </div>
    </div>
  );
}

Copy the code

users/index.js


import Link from 'umi/link'
import styles from './index.css';

export default function() {
  return (
    <div className={styles.normal}>
      <h1>List of users</h1>
      <Link to='/users/1'>User 1</Link>
      <Link to='/users/2'>The user 2</Link>
    </div>
  );
}

Copy the code

Access localhost: 8000 / users

Click user 1 to see the effect

Click user 2 to see the effect

Configuration routing

Create the config/config.js configuration file in the root directory. The existence of this configuration item does not resolve the reductive form of the Pages directory

export default {
    // Component is relative to the root directory /pages
    routes: [{path: '/'.component: './index' },
        {
            path: '/users'.component: './users/_layout'.routes: [{path: '/users/'.component: './users/index' },
                { path: '/users/:id'.component: './users/$id'}]},],};Copy the code

404 routing

The convention pages/404.js is 404 pages,

Added in route configuration

export default {
    // Component is relative to the root directory /pages
    routes: [{path: '/'.component: './index' },
        {
            path: '/users'.component: './users/_layout'.routes: [{path: '/users/'.component: './users/index' },
                { path: '/users/:id'.component: './users/$id'}]}, {components:'./404.js'}]};Copy the code

Permissions routing

config/config.js

export default {
    // Component is relative to the root directory /pages
    routes: [{path: '/'.component: './index' },
        // The convention is capitalized Routes
        { path: '/about'.component: './about'.Routes: ['./routes/PrivateRoute.js'] {},path: '/users'.component: './users/_layout'.routes: [{path: '/users/'.component: './users/index' },
                { path: '/users/:id'.component: './users/$id'}]}, {path: '/login'.component: './login' },
        {component:'./404.js'},]};Copy the code
Umi g page about # Generate about pageCopy the code

Create routes/ privateroute.js in the root directory

import Redirect from 'umi/redirect';

export default (props) => {
    if(Math.random() > 0.5) {return <Redirect to='/login'/>
    }
    return (
        <div>
            {props.children}
        </div>)}Copy the code
The introduction of antd
  • Add antd:npm i antd -S
  • Add umi – plugin – react:npm i umi-plugin-react -D
  • Modify the config/config. Js
 plugins: [
    ['umi-plugin-react', {
      antd: true,}]],Copy the code

page/login.js

import styles from './login.css';
import { Button } from "antd";
export default function() {
  return (
    <div className={styles.normal}>
      <h1>Page login</h1>
      <Button type='primary'>button</Button>
    </div>
  );
}

Copy the code

Effect display:

Dvajs

Dva is a data flow solution based on Redux and Redux-Saga. In order to simplify the development experience, DVA also has additional built-in React-Router and FETCH, so it can also be understood as a lightweight application framework

Features:

1.Easy to learn and use - only6API, which is especially friendly to Redux users, was reduced to0 API
2.Elm Concept - Simplifies concepts introduced by Redux and Redux-Saga through reducers, effects and Subscriptions organizational models3.Plug-in mechanisms such as dvA-loading can handle loading state automatically without having to write showLoading and hideLoading over and over again4.Support HMR - Implement COMPONENTS, routes, and Models HMR based on babel-plugin-dva-hMRCopy the code

Dva is used in UMI

page g page goods // Create the goods page
Copy the code

Config /config.js Modifies the configuration

export default {
    // Component is relative to the root directory /pages
    routes: [{path: '/'.component: './index' },
        { path: '/goods'.component: './goods'}, # add position {path: '/about'.component: './about'.Routes: ['./routes/PrivateRoute.js'] {},path: '/users'.component: './users/_layout'.routes: [{path: '/users/'.component: './users/index' },
                { path: '/users/:id'.component: './users/$id'}]}, {path: '/login'.component: './login' },
        { component: './404.js'},].plugins: [['umi-plugin-react', {
            antd: true.dva: true}]],};Copy the code

Configuration models

Create models/goods. Js

export default {
    namesapce: "goods".// The namespace of the model to distinguish multiple models
    state: [{ title: 'Web Architecture' }, { title: 'Python Architecture class'}].// Initial state
    reducers: {addGood(state,action){
            return [...state,{title:action.payload.title}]
        }
    }, // Update the status
    effects: { // Side effects operate asynchronously}},Copy the code

Configuration goods. Js

import { Component } from 'react';
import styles from './goods.css';
import { connect } from "dva";
import { Card, Button } from "antd";

@connect(
  state= > ({
    goodsList: state.goods  // Gets the model state of the specified namespace{}),addGood: title= > ({
      type: 'goods/addGood'.// The action type must be prefixed with the namespace and reducer name
      payload: { title }
    }),
  }
)
export default class extends Component {
  render() {
    return (
      <div className={styles.normal}>
        <h1>Page goods</h1>
        <div>
          {
            this.props.goodsList.map(good => {
              return (
                <Card key={good.title}>
                  <div>{good.title}</div>
                </Card>)})}</div>
        <div>
          <Button onClick={()= >This.props. AddGood (' props' + new Date().getTime())}> Add props</Button>
        </div>
      </div>); }}Copy the code

Simulated Mock

Create a mock/goods. Js


let data = [ // Initial state
    {
        title: 'Web Architecture'
    },
    {
        title: 'Python Architecture class'}];export default {
    "get /api/goods": function (req, res) {
        setTimeout(() = > {
            res.json({ result: data });
        }, 1000); }}Copy the code

models/goods.js

import axios from 'axios'
function getGoods() {
    return axios.get('/api/goods')}export default {
    namesapce: "goods".// The namespace of the model to distinguish multiple models
    state: [].// Initial state
    reducers: {addGood(state,action){
            return [...state,{title:action.payload.title}]
        },
        initGoods(state,action){
            return action.payload
        }
    }, // Update the status
    effects: { // Side effects operate asynchronously
        *getList(action, { call, put }) {
            const res = yield call(getGoods);
            // The name of type does not require a namespace
            yield put({ type: 'initGoods'.payload: res.data.result })
        }
    },
}
Copy the code

Goods. Js modified

import { Component } from 'react';
import styles from './goods.css';
import { connect } from "dva";
import { Card, Button } from "antd";

@connect(
  state= > ({
    goodsList: state.goods  // Gets the model state of the specified namespace{}),addGood: title= > ({
      type: 'goods/addGood'.// The action type must be prefixed with the namespace and reducer name
      payload: { title }
    }),
    getList: () = > ({
      type: 'goods/getList',})})export default class extends Component {
  componentDidMount() {
      / / call
    this.props.getList()
  }
  render() {
    return (
      <div className={styles.normal}>{/ * * /}</div>); }}Copy the code

** Loading state :** Implemented using built-in DVa-loading

  • Gets the loading state,goods.js
@connect(
  state= > ({
    loading:state.loading }), { ... })export default class extends Component {
    render(){
        if(this.props.loading.models.goods){
            return <div>In the loading...</div>}... }}Copy the code