- Source code github repository (state-management-compare) master master branch horizontal comparison of front-end development in different state management schemes
- The original article appears in Readux 202008
- Update record
- Updated on 22 August 2020
- First published on 16 April 2020
[TOC]
directory
-
introduce
-
counter
introduce
An overview of the
Redux is a predictable state container for JavaScript applications (size 2KB)
Installation tools
yarn add @reduxjs/toolkit
yarn add redux
npm install --save-dev redux-devtools
Copy the code
A simple case
// action type
const ADD = "ADD";
// action creator
const counterActionCreator = () = > ({
type: ADD,
payload: {},});// init State
const initState = {
number: 0.list: [{id: "1"],},info: {
userName: "yayxs",}};// A reducer
const counterReducer = (initState, counterActionCreator()) = > {};
// create store
let store = createStore(counterReducer);
// subscribe
store.subscribe(() = > console.log(store.getState()));
//change state
store.dispatch(counterActionCreator());
Copy the code
Action
An action is an action that moves data from an application to the warehouse, the data warehouse
-
JS object
-
format
{ type:ADD, // The type field is the convention, which means the name of the action, index:1.content:'This is a little bit of content.' } Copy the code
- The object’s
type
The field represents the action performed; String constant - Index A unique ID, mainly an identifier
- A content (or other) JS object can have any field, just one field
- The object’s
In real development we are used to creating the action function
const addAction = (params) = > {
return {
type: ADD, ... params, }; };Copy the code
Reducer
Now we still don’t talk about store, now we have action, but action it just describes the action, but it doesn’t know how to update the data, and when it comes to data, let’s say
{
num: 0;
}
Copy the code
This simple JS object is data
ACTION is a normal object; REDUCER is a normal function
-
Say ordinary also not ordinary, js function only
-
function(a,b){ console.log(a+b) } Copy the code
-
But it’s not that simple
-
-
Clean and simple,
-
// Default data const initData = { num: 123};// reducer const counterReducer = (state = initData, action) = > { // Do nothing, return the state passed in (initData by default) return state; }; Copy the code
-
How is it possible to do nothing
import { addAction } from "./actions";
// Default data
const initData = {
num: 123
};
// reducer
const counterReducer = (state = initData, action) = > {
// What type of action is passed in
switch (action.type) {
case addAction:
return Object.assign({}, state, {
...
});
default:
return state;
}
// Do nothing, return the state passed in (initData by default)
// return state;
};
Copy the code
Pay attention to
- You cannot modify incoming data
- By default, you must return the old one
state
Store
- This is the state store, maintaining the state
- The getState() method gets the state
- Provide the dispatch () method to send the action
- Subscribe () to register listening
To obtain state
getState();
Copy the code
Update the status
dispatch(action);
Copy the code
That’s what we call handing out an action
Register to listen (subscribe)
subscribe(listener);
Copy the code
The three principles
-
** Single source of truth**
-
{ "number": 123."obj": {}, "list": []}Copy the code
-
-
State is read-only (** state is read-only**)
-
** Changes are made with pure functions**
Part of the
- state
- Server data
- UI data
- app state
- Action
- Reducer
- Store
counter
At this point, the question is, with all this, how do we create this repository
yarn add redux
Copy the code
There’s a method in this library, which we call redux
Build the action
import { ADD_TYPE } from "./actionTypes";
const actionCreator = (params) = > ({
type: ADD_TYPE,
payload: {
...params,
},
});
export { addAction };
Copy the code
Build the reducer
import { actionCreator } from "./actions";
// Default data
const InitialState = {
info: {
counter: 0,}};// reducer
const counterReducer = (state = { num: 123 }, action) = > {
// What type of action is passed in
switch (action.type) {
case addAction:
return Object.assign({}, state, action);
default:
return state;
}
// Do nothing, return the state passed in (initData by default)
// return state;
};
export { counterReducer };
Copy the code
Create the store
The introduction of the file
import { createStore } from "redux";
import { counterReducer } from "./reducers";
Copy the code
createStore
const store = createStore(
counterReducer /* preloadedState, */.window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
Copy the code
Distributing the action
const handleClick = () = > {
console.log('Clicked the button');
const action = addAction({ num: "456" });
store.dispatch(action);
};
Copy the code
Listening to the
useEffect(() = > {
store.subscribe(() = > {
console.log("-- -- -- -- --", store.getState()); }); } []);Copy the code
Changes to the subscription status
const render = () = > {
ReactDom.render(<App />.document.querySelector("#root"));
};
// Render once on the way up
render();
// Subscribe to changes and re-render every time the data changes
store.subscribe(render);
Copy the code
summary
Through a simple case, we know a simple process:
- First build a
action
Return an objectThere must be a Type attribute - build
reducer
The response action T returns the data back to the Store via a return - using
redux
This library creates a store to pass writtenreducer
- By using the
$store.subscribe()
Register to monitor - Can be achieved by
store.getState()
The values
React-Redux
That looks fine in the redux library we used above, but
- The first step is to import store
- And then sign up to listen
- We then cancel listening when the component is destroyed
React-redux: React-redux: React-redux: React-redux: React-redux: React-Redux: React-Redux
What is thereact-redux
-
Official product of Redux
-
React is used to manage data
The Provider component
- Wrap around the outermost component so that all child components can be reached
state
- receive
store
As aprops
throughcontext
pass
<Provider store={store}>
<App />
</Provider>
Copy the code
mapStateToProps(state,ownProps)
const mapStateToProps = (state, ownProps) = > {
console.log(state);
return state;
// return {
// prop: state.prop
// }
};
Copy the code
mapDispathToProps(dispath,ownProps)
const mapDispatchToProps = (dispatch, ownProps) = > {
return {
sendAction: () = > {
dispatch({
type: "ADD_TYPE"}); }}; };Copy the code
The connect method
- Component Internal Acquisition
store
In thestate
- through
connect
To strengthen
export default connect(
(state, { history, location, match, staticContext }) = > ({
...state,
}),
(dispatch, { history, location, match, staticContext }) = > ({
add() {
dispatch(
actionCreator({
num: 10,})); }, }) )(Home);Copy the code
The complete code
import React, { memo, useEffect } from "react";
import axios from "axios";
import { connect } from "react-redux";
const ADD_TYPE = "ADD_TYPE";
const actionCreator = (params) = > ({
type: ADD_TYPE,
payload: {
...params,
},
});
const InitialState = {
counter: 0};export const counterReducer = (state = InitialState, { type, payload }) = > {
switch (type) {
case ADD_TYPE:
console.log(state);
return {
...state,
counter: state.counter + payload.num,
};
default:
returnstate; }};const Home = ({ history, location, match, staticContext, counter, add }) = > {
useEffect(() = > {
return () = >{}; } []);const handleClick = (e) = > {
add();
};
return (
<>
<p>This is the component</p>
<h3>{counter}</h3>
<button
onClick={(e)= >{ handleClick(e); }} > Click love</button>
</>
);
};
export default connect(
(state, { history, location, match, staticContext }) = > ({
...state,
}),
(dispatch, { history, location, match, staticContext }) = > ({
add() {
dispatch(
actionCreator({
num: 10,})); }, }) )(Home);Copy the code
use
-
Install related dependencies
-
Build store and Readucer
-
Provider component implementation
<>
<Provider store={store}>
<List></List>
<Detail></Detail>
</Provider>
</>
Copy the code
- connect
combineReducers
- Function, which takes one argument
- Split reducer
import { createStore, combineReducers } from "redux";
// import { counterReducer } from "./reducers";
// import rootReducer from './reducers/index'
import { infoReducer } from "./reducers/infoReducer";
import { listReducer } from "./reducers/listReducer";
const reducer = combineReducers({
infoReducer,
listReducer,
});
/ / build a store
const store = createStore(reducer);
export default store;
Copy the code
Create components
-
ComA A component
import React, { Component } from "react"; import { connect } from "react-redux"; class ComA extends Component { handleClick = () => { this.props.getInfo(); }; Render () {return (< div > {/ * < h3 > {this. Props.} < / h3 > * /} < button onClick = {this. HandleClick} > information < / button > < / div >). } } const mapStateToProps = (state, ownProps) => { console.log(state.infoReducer); // return { // prop: state.prop, // }; // return state return { ... state.infoReducer, }; }; const mapDispatchToProps = (dispatch, ownProps) => { return { getInfo: () => { const actionCreator = { type: "GET_INFO", }; dispatch(actionCreator); }}; }; export default connect(mapStateToProps, mapDispatchToProps)(ComA);Copy the code
-
ComB
import React, { Component } from "react"; import { connect } from "react-redux"; class ComB extends Component { handleClick = () => { this.props.getList(); }; Render () {return (<div> <button onClick={this.handleclick}> get list </button> </div>); } } const mapStateToProps = (state, ownProps) => { console.log(state.listReducer) // return state return { ... state.listReducer } }; const mapDispatchToProps = (dispatch, ownProps) => { return { getList: () => { const actionCreator = { type: "GET_LIST", }; dispatch(actionCreator); }}; };Copy the code
export default connect(mapStateToProps, mapDispatchToProps)(ComB);
! [](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/4/16/17180a5f0b412611~tplv-t2oaga2asx-image.image ) - infoReducer.js ```js const info = { name: "yayxs", }; const infoReducer = (state = {}, action) => { switch (action.type) { case "GET_INFO": return { ... info, }; default: return state; }}; export { infoReducer };Copy the code
-
listReducer
const listArr = [ { id: 1.con: "Headphones",},];const listReducer = (state = {}, action) = > { switch (action.type) { case "GET_LIST": return { listArr: [...listArr], }; default: returnstate; }};export { listReducer }; Copy the code
Three, the story – Saga
Anyway, as mentioned above, data flow operations only support synchronous operations, which requires middleware to implement asynchronous operations
1. The middleware
- It’s a function in itself
- Apply after the Action is published
Summary of 2.
- To manage side effects, which include things like
Asynchronous operations
To make the implementation of side effects easier - Es6 grammar, refer to Ruan teacher
3. createSagaMiddleware
The source code looks like this
export default function createSagaMiddleware<C extends object> (options? : SagaMiddlewareOptions
) :SagaMiddleware<C>;
export interface SagaMiddlewareOptions<C extends object = {}> {
/** * Initial value of the saga's context. */context? : C;/** * If a Saga Monitor is provided, the middleware will deliver monitoring * events to the monitor. */sagaMonitor? : SagaMonitor;/** * If provided, the middleware will call it with uncaught errors from Sagas. * useful for sending uncaught exceptions to error tracking services. */onError? (error:Error.errorInfo: ErrorInfo): void;
/** * Allows you to intercept any effect, resolve it on your own and pass to the * next middleware. */effectMiddlewares? : EffectMiddleware[]; }Copy the code
The import
import createSagaMiddleware from "redux-saga";
Copy the code
Building a store
const store = createStore(sagaReducer, {}, applyMiddleware(sagaMiddleware));
Copy the code
- The first parameter is reducer
- The second initState
- Third parameter: middleware
perform
sagaMiddleware.run(defSage);
Copy the code
Case 4.
Saga’s helper function
-
takeEvery
-
takeLatest
-
throttle
-
SagaCom
handleClick = (type) = > {
switch (type) {
case "takeEvery":
this.props.dispatch({
type: "takeEvery"});break;
case "takeLatest":
this.props.dispatch({
type: "takeLatest"});break;
case "throttle":
this.props.dispatch({
type: "throttle"});break;
default:
break; }};Copy the code
- sages/index.js
import {
takeEvery,
takeLatest,
throttle,
select,
call,
} from "redux-saga/effects";
import axios from "axios";
export function* defSage() {
yield takeEvery("takeEvery".function* () {
const state = yield select((state) = > state.payload);
const res = yield call(
axios.post,
`http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(res);
});
// Last time, cancel is running
yield takeLatest("takeLatest".function* () {
const state = yield select((state) = > state.payload);
const res = yield call(
axios.post,
`http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(res);
});
/** * milliseconds */
yield throttle(0."throttle".function* () {
const state = yield select((state) = > state.payload);
const res = yield call(
axios.post,
`http://rap2.taobao.org:38080/app/mock/249413/mock-api/v1/users/login`,
{
...state,
}
);
console.log(res);
});
}
Copy the code
Effect creator
Detailed API usage can be found in the official documentation
- select
- call
- take
- put
The business process
To get the data
- As soon as the page loads, then send one to get the data
action
- in
reducer
If the action matches the corresponding action, the data is returned directly - Used in Saga
takeEvery
To listen in. - The call method invokes an asynchronous request, passing in the parameters of the request
- Put Side Effect Indicates whether sending the action succeeds or fails
- Process the actions in the Reducer
The life cycle
- ComponentDidMount gets data
- ComponentWillUpdate handles data
Four, thinking
- Hooks API: how do functional components listen for page data changes and then refresh?
- Helper functions in Redux-Saga
takeEvery
takeLatest
throttle
What’s the difference at the bottom?
Thank you for seeing this. Might as well give me a star. Thank you