As a “VUE engineer”, I was required to develop the React app when I first joined the company. Due to the arrangement of the company, I spent a week or two on the React website, and was barely able to write it. Redux is one of the most frustrating things to learn about React. What the hell is redux? I can learn it quickly, but Redux can’t understand it. Once doubted their intelligence, but turned to think, in addition to read and write several times, how can?
Without further ado, today I will briefly introduce the simple use of Redux, not BB, open the whole!
Source code portal
Redux’s three principles
- Single data source
The state of the entire application is stored in an object tree that exists in only one store.
- State is read-only
The only way to change state is to trigger an action, which is a generic object that describes events that have occurred.
- Use pure functions to perform modifications
To describe how actions change the state tree, you need to write reducers that, depending on the action, return the new state
One-way data flow for REdux
Basic use of Redux
1.# NPM NPM install redux # Yarn Yarn add redux2.Create the store// store/store.js
import { createStore } from 'redux'
import counterReducer from './reducer'
let store = createStore(counterReducer)
export default store
3.Create a Reducer pure function// store/reducer.js
function counterReducer (state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
export default counterReducer
/ / create the action
// store/actions.js
export function increment () {
return { type: 'INCREMENT'}}export function decrement () {
return { type: 'DECREMENT'}}// Redux store consumption: ReduxDemo components
import React, { useState, useEffect } from 'react'
import logo from './logo.svg';
import * as css from './App.module.scss';
import { Button } from 'antd-mobile';
import store from './store/store'
import { increment, decrement } from './store/actions'
function ReduxDemo () {
const [count, setCount] = useState(0)
useEffect(() = > {
// Set the initial count value
setCount(store.getState())
// Subscribe to store changes and unsubscribe when the component is destroyed
const unsubscribe = store.subscribe(() = > setCount(store.getState()))
return () = > {
unsubscribe()
}
}, [])
return (
<div className={css.App}>
<header className={css['App-header']} >
<img src={logo} className={css['App-logo']} alt="logo" />
Redux Demo
</header>
<p className={css.count}>
{count}
</p>// Disrement an action such as {type: 'DECREMENT'} can also be set to a value, and // such as {type: 'DECREMENT', payload: 10}<Button onClick={()= >Store. Dispatch (increment ())} > click + 1</Button>
<Button onClick={()= >Store. Dispatch (decrement ())} > click - 1</Button>
</div>
);
}
export default ReduxDemo;
Copy the code
666, operational!
What if the action is an asynchronous operation? After all, it’s quite common to do Ajax requests in an action. Ok, so we’re starting to change Decrement:
export function decrement () {
// return { type: 'DECREMENT' }
setTimeout(() = > {
return { type: 'DECREMENT'}})}Copy the code
Unfortunately, clicking -1 raises Error: Actions must be plain objects. Use custom middleware for Async Actions. This is because Redux only supports synchronous operations and requires middleware such as Redux-Chunk for asynchronous operations
export function decrement () {
// return { type: 'DECREMENT' }
// setTimeout(() => {
// return { type: 'DECREMENT' }
// })
return dispatch= > {
setTimeout(() = > {
dispatch({
type: 'DECREMENT',}}),1000)}}Copy the code
Ok, everything is fine, perfect.
To use redux in a component, you have to manually subscribe to store changes and remember to remove the subscription after the component is uninstalled. In order to make Redux work better with React, react-Redux has been developed in the community.
Basic use of React-redux
// The installation of the NPM package will not be described here
1.Wrap the Provider around the root component// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import ReduxDemo from './ReduxDemo';
import ReactReduxDemo from './ReactReduxDemo';
//
enables connect() methods at the component level to obtain the Redux store.
// Normally, the root component should be nested within
to use the connect() method.
import { Provider } from "react-redux"
import store from './reactReduxStore/store'
ReactDOM.render(
<React.StrictMode>{/ *<ReduxDemo />* /}<Provider store={store}>
<ReactReduxDemo />
</Provider>
</React.StrictMode>.document.getElementById('root'));2.Create the store// reactReduxStore/store.js
import { createStore, applyMiddleware } from 'redux'
import counterReducer from './reducer'
// redux-thunk supports async
import thunk from 'redux-thunk'
let store = createStore(counterReducer, applyMiddleware(thunk))
export default store
// Create a reducer pure function
// reactReduxStore/reducer.js
function counterReducer (state = 0, action) {
switch (action.type) {
case 'INCREMENT':
// action takes parameters
return state + action.payload
case 'DECREMENT':
return state - 1
default:
return state
}
}
export default counterReducer
3.Create the actions// reactReduxStore/actions.js
export function increment (payload) {
// action takes parameters
return { type: 'INCREMENT', payload }
}
export function decrement () {
/ / asynchronous
return dispatch= > {
setTimeout(() = > {
dispatch({
type: 'DECREMENT',}}),1000)}}4.Redux Store consumption: ReactReduxDemo components// ReactReduxDemo.js
import React, { useState, useEffect } from 'react'
import logo from './logo.svg';
import * as css from './App.module.scss';
import { Button } from 'antd-mobile';
import { connect } from 'react-redux' // Connect the component to redux
import * as counterActions from './reactReduxStore/actions'
import { bindActionCreators } from 'redux' // Merge multiple actions
function ReactReduxDemo (props) {
const { counterActions: { increment, decrement }, count } = props
return (
<div className={css.App}>
<header className={css['App-header']} >
<img src={logo} className={css['App-logo']} alt="logo" />
ReactRedux Demo
</header>
<p className={css.count}>
{count}
</p>
<Button onClick={()= >Increment (2)}> Increment (2</Button>
<Button onClick={()= >Decrement ()}> Click: asynchron-1</Button>
</div>
);
}
// incorporate state into the props of the component
const mapStateToProps = (state) = > {
return {
count: state
}
}
// when action is less
// const mapDispatchToProps = (dispatch) => {
// return {
// increment: () => { dispatch(increment()) },
// decrement: () => { dispatch(decrement()) }
/ /}
// }
// Action is more verbose
// incorporate dispatch into props
const mapDispatchToProps = (dispatch) = > {
return {
counterActions: bindActionCreators(counterActions, dispatch)
}
}
// mapStateToProps, mapDispatchToProps cannot be switched in sequence
// Connect uses higher-order components to incorporate state and dispatch into components
export default connect(mapStateToProps, mapDispatchToProps)(ReactReduxDemo)
Copy the code
Working fine, good!
From the perspective of the implementation, it’s a bit cumbersome, so what if we don’t want to introduce React-Redux? Fortunately, we can implement redux-like functionality ourselves based on the context and useReducer.
Context and useReducer implement the Redux function
In the implementation process found a relatively embarrassing problem, that is “action can not be asynchronous “(in the premise of not using asynchronous solution) operation problem exposed processing, how to solve?
If you think about it, since Dispatch can only handle synchronous tasks, why not satisfy it? So how to satisfy? Or dispach as an argument to dispach or action.js; otherwise, we could not dispach the mouse or dispach as an argument to dispach.
/ / create CustomContext
// customRedux/context.js
import React from 'react'
const CustomContext = React.createContext();
export default CustomContext
/ / create the actions
// customRedux/actions.js
export function increment (dispatch, payload) {
// action takes parameters
dispatch({ type: 'INCREMENT', payload })
}
export function decrement (dispatch) {
/ / asynchronous
setTimeout(() = > {
dispatch({ type: 'DECREMENT'})},1000)}export function toggleUser (dispatch) {
dispatch({ type: 'TOGGLE_USER'})}/ / create a reducer
// customRedux/reducer.js
Reducer must be a pure function
export const initalState = { count: 0.user: 'Joe' }
function rootReducer (state = initalState, action) {
console.log(action);
switch (action.type) {
case 'INCREMENT':
// action takes parameters
return { ...state, count: state.count + action.payload }
case 'DECREMENT':
return { ...state, count: state.count - 1 }
case 'TOGGLE_USER':
return { ...state, user: state.user === 'Joe' ? 'bill' : 'Joe' }
default:
return state
}
}
export default rootReducer
/ / useReducer useContext
import React, { useContext, useReducer } from 'react'
import logo from './logo.svg';
import * as css from './App.module.scss';
import { Button } from 'antd-mobile';
import { increment, decrement, toggleUser } from './customRedux/actions'
import CustomContext from './customRedux/context'
import rootReducer, { initalState } from "./customRedux/reducer"
function CustomReduxWrapper () {
const [state, dispatch] = useReducer(rootReducer, initalState);
return (
<CustomContext.Provider value={[state, dispatch]} >
<CustomRedux />
</CustomContext.Provider>)}function CustomRedux (props) {
const [state, dispatch] = useContext(CustomContext)
return (
<div className={css.App}>
<header className={css['App-header']} >
<img src={logo} className={css['App-logo']} alt="logo" />
CustomRedux Demo
</header>
<p className={css.count}>{state.count}</p>
<Button onClick={()= >Increment (Dispatch, 2)}> Click +2</Button>
<Button onClick={()= >Decrement (dispatch)} > click - 1</Button>
<p className={css.user}>User: {state. The user}</p>
<Button onClick={()= >ToggleUser (dispatch)}> Switch users</Button>
</div >
);
}
export default CustomReduxWrapper;
Copy the code
Redux, react-redux, redux, react-redux, redux, react-redux, redux, react-redux, redux, redux