the relative basics of the React project have been explained in detail in 1BI 2 of this tutorial. In 3pr 4, continue to explain the advanced part of React.
Build the whole family bucket of React project from scratch (1)
Build the whole family bucket of React project from scratch (2)
before we begin, review the content introduced in the previous article:
1 create React-APP
2 streamlined items
2.1 Delete files
2.2 simplify the code
2.3 use Fragment to remove the outer tag of the component
3 Project directory structure
3.1 introduce global common styles
3.2 support Sass/Less/Stylus
4 routes
4.1 Page build
4.2 use react-router-dom
4.3 Route Jump
5 components introduce
5.1 create header components
5.2 introduce Header components
5.3 components pass parameters
6 React Developer Tools browser plug-in
in this second article, continue to share the following:
take a peek at it
7 Redux and related plug-ins
7.1 install redux
7.2 install react-redux
7.3 install redux-thunk
7.4 install the browser Redux plug-in
7.5 create store
7.6complex project store decomposition
7.7 docking react-redux with store
7.8 start Redux DevTools
7.9 install using immutable
8 Mock.js installation and use
9 solve the cross-domain problem of local development
10 other commonly used tools
11 complimentary chapter: integrated Ant Design
11.1 install Ant Design
11.2 implement on-demand loading
11.3 Custom theme colors
7 Redux and related plug-ins
students who have done vue development know that the corresponding tool for vuex,react is Redux, and of course there are some ancillary tools, such as react-redux, redux-thunk, and immutable.
redux involves a lot of content and can be shared separately. This sharing space is limited, only a brief introduction to the installation and deployment process, if you do not understand, you can skip or consult the official documents.
7.1 install redux
execute:
npm install redux --save
it's possible to just install redux, but it's cumbersome. Updating the data in store in redux requires manual subscription (subscribe) to update. You can improve development efficiency with another plug-in (react-redux).
7.2 install react-redux
execute:
npm install react-redux --save
react-redux allows data in store to be mapped to the component's props through the connect method, eliminating store subscriptions. The attributes that read store in the original state are read by props instead.
since store (Section 7.5) has not been covered yet, the usage of react-redux is described in Section 7.6.
7.3 install redux-thunk
execute:
npm install redux-thunk --save
redux-thunk allows you to return functions in actionCreators. In this way, the business logic (such as interface requests) can be written centrally in actionCreator.js, which makes the master file of the component more concise while facilitating reuse.
7.4 install browser Redux plug-in
to make it easier to track redux status, it is recommended that you install the chrome plug-in.
first surf the Internet scientifically, search for "Redux DevTools" in the chrome online app store and install it.
cannot be used directly after installation and needs to be configured in the project code. The next step is to explain.
7.5 create store
after installing the above plug-ins, you can use store to manage state data.
if the project is simple, with only one or two pages, you can create only one total store to manage the overall project. The directory structure is referenced as follows:
├─ /src
+ | ├─ /store
+ | | ├─ actionCreators.js
+ | | ├─ contants.js <-- Defining constants for methods
+ | | ├─ index.js
+ | | └─ reducer.js
the following is a code example for each file:
src/store/actionCreators.js:
import * as constans from './constants'
export const getData = (data) => ({
type: constans.SET_DATA,
data
})
src/store/contants.js:
export const SET_DATA = 'SET_DATA'
src/store/index.js:
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
// Here's how to get the project to support the browser plugin Redux DevTools
const composeEnhancers = typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
const store = createStore(
reducer,
enhancer
)
export default store
the above is the core code of store and supports Redux DevTools. At the same time, make use of the integration middleware (applyMiddleware) function of redux to integrate redux-thunk, and finally create store.
src/store/reducer.js:
import * as constants from './constants'
// Initial default state
const defaultState = {
myData: null
}
export default (state = defaultState, action) => {
// As state is a reference type, it cannot be modified directly, otherwise it would not be monitored for changes to state. Therefore, you need to make a copy of it and then return the new state.
let newState = Object.assign({}, state)
switch(action.type) {
case constants.SET_DATA:
newState.myData = action.data
return newState
default:
return state
}
}
above code, we set up a myData in store. How to better solve the problem of state modification will be mentioned in section 7.8.
7.6 complex project store decomposition
for projects with more pages, if the data is concentrated in one store, the maintenance cost is very high. Let's share how to decompose store into various components.
generally speaking, each component has its own store, and then the store under src is used as the total set to integrate the store of each component.
take the header and login components as examples to create their own store.
The store directory structure of
header is as follows:
| | ├─ /components
| | | ├─ /header
+ | | | | ├─ /store
+ | | | | | ├─ actionCreators.js
+ | | | | | ├─ contants.js
+ | | | | | ├─ index.js
+ | | | | | └─ reducer.js
The index.js code under
component store is as follows:
import reducer from './reducer'
import * as actionCreators from './actionCreators'
import * as constants from './constants'
export { reducer, actionCreators, constants}
is actually a collection of other files under the component store as a unified output.
The contants.js code under
component store is as follows:
const ZONE = 'components/header/'
export const SET_DATA = ZONE + 'SET_DATA'
ZONE is used to avoid contants duplicates with other components.
in the same way, create a store under login (I won't repeat it).
then modify the total store under the project src. The directory structure changes as follows:
├─ /src
| ├─ /store
- | | ├─ actionCreators.js <-- Delete
- | | ├─ contants.js <--Delete
| | ├─ index.js
| | └─ reducer.js
src/store/index.js is rewritten as follows:
import { combineReducers } from 'redux'
import { reducer as loginReducer } from '../pages/login/store'
import { reducer as headerReducer } from '../components/header/store'
const reducer = combineReducers({
login: loginReducer,
header: headerReducer
})
export default reducer
The purpose of the
above code is to introduce the store of login and header, then merge them together through combineReducers, and add unique object key values, respectively.
this benefit is obvious:
- avoid contamination of store data of various components
- components maintain their own store independently to reduce maintenance costs
it is highly recommended to maintain store in this way.
7.7 docking react-redux with store
to make it easier for every component to use store instead of referencing store over and over again. Next, let's dock react-redux and store.
modify src/index.js:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
+ import { Provider } from 'react-redux'
+ import store from './store'
import './common/style/frame.styl'
+ const Apps = (
+ <Provider store={store}>
+ <App />
+ </Provider>
+ )
M ReactDOM.render(Apps, document.getElementById('root'))
the above code passes the store to the entire App using the Provider provided by react-redux.
in components that need to use store, you wrap the component using the connect method provided by react-redux.
take login as an example, modify src/pages/login/index.js:
import React, { Component } from 'react'
import Header from '../../components/header'
+ import { connect } from 'react-redux'
+ import * as actionCreators from './store/actionCreators'
import './login.styl'
class Login extends Component {
render() {
return (
<div className="P-login">
<Header />
<h1>Login page</h1>
+ <p>login: myData = {this.props.myData}</p>
+ <button onClick={()=> {this.props.getData('123456')}}>Change myData for login</button>
<button onClick={this.gotoHome.bind(this)}>Jump to Home page</button>
</div>
)
}
gotoHome() {
this.props.history.push('/home')
}
}
+ // Mapping data from the store to the component's props
+ const mapStateToProps = (state) => ({
+ myData: state.getIn(['login', 'myData']),
+ })
+ // Mapping store dispatches to component props
+ const mapDispatchToProps = (dispatch) => ({
+ getData(data) {
+ const action = actionCreators.getData(data)
+ dispatch(action)
+ }
+ })
M export default connect(mapStateToProps, mapDispatchToProps)(Login)
The biggest change in
is the last line of code, wrapped by the connect method.
then map the state and dispatch in store to the props of the component. This can be accessed directly through props, and changes in data in store will directly change the props and trigger the view update of the component.
after clicking the button, you can see that the myData displayed on the page has changed.
Let's do a visual tracking view through Redux DevTools.
7.8 start Redux DevTools
after the setting of Section 7.5, the Redux DevTools of Section 7.4 can be used normally. Click on the icon in the upper right corner of the browser, and in the panel that appears, you can trustfully track and view the changes of the data in store, which is very convenient.
you can also start Redux DevTools through the debugging toolbar:
specific usage will not be discussed here.
7.9 install using immutable
in section 7.5, it is mentioned that state cannot be modified directly in store, because state is a reference type, and direct modification may result in undetected data changes.
immutable.js literally understands that immutable means "immutable". Data created with immutable is immutable, and any modification to immutable data returns a new immutable data without changing the original immutable data.
immutable.js provides a number of ways to easily modify referenced data of object or array types.
install immutable and redux-immutable, execute:
npm install immutable redux-immutable --save
then modify the code:
src/store/reducer.js:
- import { combineReducers } from 'redux'
+ import { combineReducers } from 'redux-immutable'
...(summary)
the above code is to change combineReducers into redux-immutable.
then modify src/pages/login/store/reducer.js
import * as constants from './constants'
+ import { fromJS } from 'immutable'
M const defaultState = fromJS({
myData: null
M })
+ const getData = (state, action) => {
+ return state.set('myData', action.data)
+ }
export default (state = defaultState, action) => {
switch(action.type) {
case constants.SET_DATA:
M return getData(state, action)
default:
return state
}
}
The intervention of
immutable is to convert the original JS type into immutable type by using the fromJS method.
since state is already of type immutable, you can use the set method of immutable to modify the data and return a new state. The code is much simpler and does not need to be copied and reprocessed manually through methods such as Object.assign.
The code modification for the
header component is similarly not discussed.
there are many other unusual ways to use immutable. For more information, please refer to the official documentation: