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.
enhancer
is a high-level function used to enhance the created store. Its parameter iscreateStore
, which returns a more powerful store generation function. (Function similar to middleware).- 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 usingmiddleware
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
Navigate, route redirection
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