react family bucket (2)

Directory

redux principle

createStore()

ComBineReducers()

applyMiddleware()

react-router

nested routes

Navigate, route redirection

redux-saga

UmiJs


Redux principle

The way to create Store in redux is createStore(), so what is the principle of createStore()?

createStore()

Let’s first look at how the store is created

import { createStore, combineReducers, applyMiddleware } from 'redux'
import counter from '../reducer/counter'
import thunk from 'redux-thunk'
import logger from 'redux-logger'
// create reducer specific state executor
const store = createStore(
  combineReducers({ counter }),
  applyMiddleware(logger, thunk)
)
export default store

It can be seen that createStore() receives two parameters, one is combineReducers(), and the second is the functional plug-in merge applyMiddleware.

import {createStore, combineReducers,
applyMiddleware} from redux
store. getState()
this.unsubscribe = store.subscribe()
this. unsubscribe()

createStore is a function that receives three parameters reducer, preloadedState,enhancer.

  1. enhancer is a high-level function used to enhance the created store. Its parameter is createStore, which returns a more powerful store generation function. (Function similar to middleware).
  2. The storeCreator in our mobile warehouse can actually be regarded as an enhancer. When creatingStore, saga is incorporated into it, but it is not completed as the third parameter of createStore, but using middleware is complete.
export default function createStore(reducer, preloadedState, enhancer) {
    
if (typeof preloadedState === 'function' & amp; & amp; typeof enhancer === 'function'
    || typeof enhancer === 'function' & amp; & amp; 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 parameter preloadedState is passed, and the second parameter is not a function, then save the preloadedState in the internal variable currentState, which is the default state we give to State
    if (typeof preloadedState === 'function' & amp; & amp; typeof enhancer === 'undefined') {
        enhancer = preloadedState;
        preloadedState = undefined;
    }

    if (typeof enhancer !== 'undefined') {
        if (typeof enhancer !== 'function') {
            throw new Error('Expected the enhancer to be a function.');
        }
        // createStore is used as the parameter of enhancer, returns an enhanced createStore, and then pass reducer, preloadedState in to generate store
        return enhancer(createStore)(reducer, preloadedState);
    }
//The first parameter reducer must be passed and must be a function, otherwise Redux will report an error
    if (typeof reducer !== 'function') {
        throw new Error('Expected the reducer to be a function.');
    }
// A state tree is saved inside the warehouse. can be of any type
let currentState = preloadedState;
let currentListeners=[];
    let currentReducer = reducer
function getState() {
return JSON. parse(JSON. stringify(state));
}
//Components can dispatch actions to the warehouse
function dispatch(action) {
//Call the reducer to process, get the old state, and calculate the new state
currentState = currentReducer(currentState, action);
// Notify other components to execute
currentListeners.forEach(l=>l());
}
//If other components need to subscribe to the state change time,
function subscribe(listener) {
        // Put the listening function into a queue
currentListeners. push(listener);
return function () {
currentListeners = currentListeners. filter(item=>item!==listener);
}
}
    //Initialization operation
dispatch({type:'@@INIT'});
return {
getState,
dispatch,
subscribe
}
}

ComBineReducers()

why would?
combineReducers
Woolen cloth
:
because it separates the corresponding logic

function combineReducer(reducers) {
 // The ?th one is just to filter ?pass first to filter out the reducer of ?function
 const reducerKeys = Object. keys(reducers)
 const finalReducers = {}
 reducerKeys. forEach((key) => {
     if(typeof reducers[key] === 'function') {
         finalReducers[key] = reducers[key]
     }
 })
 const finalReducersKeys = Object.keys(finalReducers) ['product','user']
 // The most important step is to combine all reducers together
 // Adjust each reducer according to the key, and merge their values together
 let hasChange = false;
 const nextState = {};
 //Is the combind function a reducer?
 return function combine(state={}, action) {
     finalReducersKeys. forEach((key) => {
 // The first step is to get the previous state[key], so the key of the reducer === the key of the store
         const previousValue = state[key];//user: {name:''}
 // Process with ?reducer[key] to get the next ? state
         const nextValue = reducers[key](previousValue, action);
 // nextValue={name:'kiki'}
 // Update the value of the store according to the key
         nextState[key] = nextValue;
         hasChange = hasChange || previousValue !== nextValue
     })
 // If the entire loop has not been updated, return state
     return hasChange ? nextState : state;
 }
}

applyMiddleware()

  • The relationship between applyMiddleware and enhancer
    • First of all, they both have the same function, both to enhance the store
    • The result of applyMiddleware is actually an enhancer
export function applyMiddleware(...middlewares){
    return (createStore) => {
        return function (...args) {
            //Create the original store
            const store = createStore(...args);
            //Get the original dispatch
            const _dispatch = store. dispatch;
            const middlewareAPI = {
                getState: store.getState,
                dispatch: (...args) => {
                    return dispatch(...args)
                }
            };
            //Call the first layer middleware
            const middlewareChain = middlewares. map( (middleware) => {
                // Let each middleware execute, pass in an object {getState, dispatch}
                return middleware(middlewareAPI);
            });
            //Through the compose composite function, first complete the current middleware, and then continue to call the next middleware,
            //And pass in the value (store.dispatch) to enhance dispatch
            _dispatch = compose(...middlewareChain)(store. dispatch);
             // return a store with enhanced dispatch
            return {
                ... store,
                dispatch: _dispatch
             };
        };
    };
}
function compose(...fns){ //[add1,add2,add3] are all functions
    if(fns. length === 0){
        return arg => arg;
    }
    if(fn2. length === 1){
        return fns[0]
    }
    return fns.reduce((f1,f2)=>(...args)=>f1(f2(...args)))
}

react-router

Install

npm install react-router-dom --save

easy to use

App.js

useRoutes() uses the routing table

import React, { useEffect } from 'react'
import {
  Link,
  useRoutes,
} from "react-router-dom"
import routes from './routes'
import './App.css'
function App() {
  const element = useRoutes(routes);
  return (
    <div className="App">
      <nav>
        <Link to="/" >Home</Link>|
        <Link to="/course" >Course</Link>|
        <Link to="/user" >User</Link>|
      </nav>
      {element}
    </div>
  )
}
export default App;

routes/index.js

routing table configuration

import NotFound from '../pages/NotFound'
import Home from '../pages/Home'
import Course from '../pages/Course'
import User from '../pages/User'
const routes = [
    {
        path:'/',
        element:<Home></Home>
    },
    {
        path:'/course',
        element:<Course></Course>
    },
    {
        path:'/user',
        element:<User></User>
    },
    {
        path:'*',
        element:<NotFound></NotFound>
    },
]
export default routes;

pages/Home.js

Do simple page rendering, same for several other pages

import React from 'react';
const Home = () => {
    return (
        <div>
            i am home page
        </div>
    );
};
export default Home;

index.js

import BrowserRouter

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from 'react-router-dom'

const root = ReactDOM.createRoot(document.getElementById('root'));
root. render(
  <BrowserRouter>
    <React. StrictMode>
      <App />
    </React. StrictMode>
  </BrowserRouter>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

running result

Nested routing

Make the following changes to the routing table

 {
    path: '/course',
    children: [
      {
        index: true, //Default is the homepage
        element: <CourseList></CourseList>,
      },
      {
        path: ':name',
        element: <CourseDetail></CourseDetail>,
      },
    ],
  },

pages/courseList.js

const CourseList =()=>{
    return <div>course list</div>
}
export default CourseList;

pages/courseDetail.js

Introduction to useEffect

import react,{useEffect}from 'react';
import {
    useParams,
    useSearchParams
} from 'react-router-dom'

const CourseDetail = () => {
    const params = useParams();
    // seaParams initializes a state, setSearchParams is used to set params
    const [searchParams, setSearchParams] = useSearchParams();
    //Use useEffect, when searchParams changes, call
    useEffect(()=>{
        console.log(searchParams.get('id'));
    },[searchParams])
    //Manually change params
    const handleUpdateParams=()=>{
        setSearchParams({
            id: "123",
            cd:"456"
        })
    }
    return <div>
            <h2>Course Details</h2>
            <button onClick={handleUpdateParams}>Change params</button>
            </div>
}
export default CourseDetail;

Run:

Enter courseDetail

Click Change, and the set parameters will appear in the address bar, and useEffect will be called to output the id

New pages/Login.js

const Login=()=>{
    return(
        <div><h1>Login interface</h1>
            <input type="text" placeholder="Please enter username" />
        </div>
        
    )
}
export default Login;

Create a new component/Auth.js to do some simple permission judgments

import { Navigate } from 'react-router-dom'
export default function Auth ({children}){
    let auth = true;
    return auth?<Navigate to='/login'></Navigate>:children
}

modify routing table

 {
        path:'/user',
        element:(
            // permission interception
            <Auth>
               <User></User>
            </Auth>
        )
    },
    {
        path:'/login',
        element:(
            <Login></Login>
        )
    },

Run:

When clicking on the user, automatically jump to the login page

redux-saga

Add store to the above example, and then try to use redux-saga

yarn add redux react-redux redux-saga

Create a new store folder to create index, user.reducer and redux.saga files in turn

store/index.js

Saga performs asynchronous operations in a synchronous manner

how to use saga

1. import createSaga Middleware from ‘redux-saga’

2.import saga from ‘./’//Import the created asynchronous action file

3. const mid = createSagaMiddleware()

4. apply Middldware(logger, mid)

5. mid. run (saga)

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

const mid = createSagaMiddleware()

const store = createStore(
  combineReducers({ user }),
  applyMiddleware(logger, mid)
)

mid. run(saga)
export default store

user.reducer

Note that the login action here uses saga, which returns a pure object action

const initState = {
  isLogin: false, //Indicates that the user is not logged in
}
function user(state = initState, action) {
  switch (action. type) {
    case 'login':
      return { ...initState, ...{ isLogin: true } }

    default:
      return initState
  }
}
export const mapStateToProps = (state) => {
  return {
    user: state. user,
  }
}
// for redux-saga pure object action
const login = () => {
  return { type: 'login_request' }
}

export const mapDispatchToProps = (dispatch) => {
  return {
    login() {
      dispatch(login())
    },
  }
}

export default user

redux.saga file action

import {call,put,takeEvery} from ‘redux-saga/effects’

mock request api

function*

import { call, put, takeEvery } from 'redux-saga/effects'
// mock request api
const request = {
  login() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (Math. random() > 0.5) {
          resolve({ code: 200, data: { id: 1, name: 'kiki' }, message: null })
        } else {
          reject({ code: 401, message: 'Unauthorized', data: null })
        }
      }, 1000)
    })
  },
}
// work saga
function* login(action) {
  try {
    const result = yield call(request.login)
    yield put({ type: 'login', result })
  } catch (error) {
    yield put({ type: 'loginError', message: error. message })
  }
}

// Associate login with saga, similar to monitoring
function* mySaga() {
  yield takeEvery('login_request', login)
}
export default mySaga

Modify Login.js

import React from 'react'
import { connect } from 'react-redux'
import { mapStateToProps, mapDispatchToProps } from '../store/user.reducer'
const Login = (props) => {
  console. log(props)
  return (
    <div>
      <input type="text" placeholder="Please enter username" />
    </div>
  )
}

export default connect(mapStateToProps, mapDispatchToProps)(Login)

Modify User.js

import React from 'react'
import { useNavigate } from 'react-router-dom'

const User = () => {
  const navigate = useNavigate()
  const handleToLinkDetail = () => {
    navigate('/course/front')
  }
  return (
    <div>
      I am a user
      <button onClick={handleToLinkDetail}>Jump to the course details page</button>
    </div>
  )
}

export default User

index.js

Introduce provider and store, pass store

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './store'
import './index.css'
import App from './App'

ReactDOM. render(
  <BrowserRouter>
    <Provider store={store}>
      <App />
    </Provider>
  </BrowserRouter>,
  document. getElementById('root')
)

UmiJs

Introduction|umiJs

DvaJs