What comes to mind when you think of Redux

  • Data state management
  • Reducer (Redux)
  • Provider Connect (React-redux)

Redux does have a lot of apis in it, but it’s not enough to just know these apis, it’s really learning how Redux manages the data flow in a project.

Therefore, in this article, I will try my best to explain this redux from three perspectives: why(this has been written before and will not be repeated) what how. There may be some places to write is not very comprehensive, there may be some places to say some mistakes, welcome to comment area correction.

The general structure of the article:

However, I will not go through the why step by step, I think you should have a general understanding of why it is out, in order to facilitate the management of data, I have written a previous article about componentalization, take you into the Raect study [detail], which talked about traditional component transfer, and why redux is needed for data management. But at the time, I didn’t understand how redux worked.

This is also an opportunity to go through the redux process. A lot of them are covered in the process.

The first statement 👇 👇 👇

In the first part, I will use Redux alone to understand some of the concepts, using a simple Todolist as an example.

In the completion section, I’ll use redux + React-Redux (or more conveniently redux) + AXIos + redux-thunk to request data asynchronously.

All right, without further ado, let’s get to work.

Lead – Master some Redux knowledge

Some key elements and workflows:

Store: A single data source that holds state, note that it is read-only

Action: Describes the change

Reducer: A function that distributes and processes changes (that is, actions) and ultimately returns the latest data to the Store

All three work together to form the Redux workflow:

As you can see from the figure, the data flow in REdux is unidirectional, and the only way data can change is if:

Actions are distributed at the View layer, which are read by our Reducer, and Reducer performs different calculations according to the content of the actions (there will be many actions), and finally generates a new state, which will be updated into the store object. It further drives the view layer to make corresponding changes.

First Experience – Walk through the process through the TODOLIST mini-project

Better understand the flow of data through these apis in Redux through a Todolist project area.

1. Initialization

  1. First, we use NPX create-react-app redux-todolist to create a new project, because we are using Redux, so we install YARN Add redux
  2. Change the code for index.js under SRC:
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList'
ReactDOM.render(
  <TodoList />.document.getElementById('root'));Copy the code
  1. Delete the original app.js and create a new todolist.js file:
import React, { Component } from 'react';

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {  }
  }
  render() { 
    return ( 
      <div>
        hello jingda
      </div>); }}export default TodoList;
Copy the code

Running the code at this point, we can successfully output Hello Jingda on the page

Let’s change the code so that the page has an input field and a submit button, and of course we can choose to initialize some list data,

The code is as follows:

import React, { Component } from 'react';
class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = { 
      list: ['1'.'2'.'3']}}render() { 
    return ( 
      <div>
        <div>
          <input placeholder="Please enter todo."></input>
          <button>submit</button>
        </div>
        <div>
          <ul>
            {
              this.state.list.map((item,index) => {
                return <li>{index} --- {item}</li>})}</ul>
        </div>
      </div>); }}export default TodoList;
Copy the code

2, create store initialization data

All we need to do is write our to-do list in the input box, press the submit button, and it goes to the list and shows up on the page.

First we create a store:

The following directory structure, why are index.js and reducer.js files under the store? As we mentioned earlier, when the view layer sends out an action, we use the reducer to process it, and when it is finished, the state will be passed to the store

So here’s the code:

Store /index.js: In this file, we reference the createStore in Redux to create a unique store that receives a reducer parameter that returns the state after the actions sent from the view layer are processed.

import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
export default store;
Copy the code

store/reducer.js:

const defaultState = {
inpuetValue:'Please enter'.list: ['jing1'.'jing2']}// Default data
export default (state = defaultState,action) => {
  return state
}
Copy the code

At this point, we have created the store for the project and initialized the data. Let’s go back to the TodoList component and reference store

import store from './store'
Copy the code

Test for the introduction of:

Print the data from the store passed in from the constructor method:

console.log(store.getState())
Copy the code

3. Trigger action

As you can see, the component is ready to fetch the data from the store. Here’s what we need to do:

1. Display the data on the page.

2. Save the input data first.

3. When the input data is input, click Submit to add a record to the list.

4. Click the delete button of each data in the list to realize the delete function.

To display store data on the page, just modify the previous code:

constructor(props) {
    super(props);
    // console.log(store.getState())
    this.state = store.getState()} 、、、、、 <ul> {this.state.list.map((item,index) = > {
          return <li>
                  {index} --- {item} 
                  <button>delete</button>
                 </li>
        	})
        }
 </ul>
Copy the code

In the meantime, we need to save the input data:

 <input
  placeholder={this.state.inpuetValue} 
onChange={this.onChangeValue.bind(this)
 </input>
Copy the code

4. Specific Redux data flow management

The next three steps all go through the following process: Create actions and send them automatically to the Reducer through store.dispatch. The Reducer processes the state data and then sends the state back to the store. I’ll write it all together.

The preparation is to bind an event to the submit and delete buttons

<button onClick={() = >AddInput ()} > submit < / button ><button onClick={(index)= >> delete this. DeleteInput (index)}</button>
Copy the code

Create actionType.js with the action type defined: actionType.js

export const CHANGE_INPUT = "CHANGE_INPUT"
export const ADD_INPUT = "ADD_INPUT"
export const DELETE_INPUT = "DELETE_INPUT"
Copy the code
  1. Create an action and distribute it
  onChangeValue(e) {
    console.log(e.target.value)
    const action = {
      type:CHANGE_INPUT,
      value:e.target.value
    }
    store.dispatch(action)
  }
  addInput() {
   
    const action = {
      type:ADD_INPUT,
    }
    store.dispatch(action)
  }
  deleteInput(index) {
    const action = {
      type:DELETE_INPUT,
      index
    }
    store.dispatch(action)
  }
Copy the code
  1. Reducer for dealing with the state
export default (state = defaultState,action) => {
  if(action.type === CHANGE_INPUT) {
    let newState = JSON.parse(JSON.stringify(state)) // Deep copy state
    newState.inpuetValue =  action.value// Index passed by the corresponding component
    //console.log(newState.value)
    return newState
  }
  if(action.type === ADD_INPUT) {
    let newState = JSON.parse(JSON.stringify(state)) // Deep copy state
    newState.list.push(action.inpuetValue) // corresponds to the value passed by our component
    return newState
  }
  if(action.type === DELETE_INPUT) {
    let newState = JSON.parse(JSON.stringify(state)) // Deep copy state
    newState.list.splice(action.index,1) // Index passed by the corresponding component
    return newState
  }
  return state
}
Copy the code

At this point we seem to have completed the steps described above, but adding data to the page is not successful at this point, and we need to do one more thing – subscribe to the Redux status.

 this.storeChange = this.storeChange.bind(this)
 store.subscribe(this.storeChange)// Subscribe to redux status
Copy the code
storeChange() {
    this.setState(store.getState())
}
Copy the code

Okay, so that’s the basic functionality, a little bit of code cleaning. We can see that on the component page, we need to create an action every time there is an event, and we need to go to the component every time we change it. Now let’s separate these actions and write them in a separate file to make it easier to manage.

In store/ actioncreators.js:

import { CHANGE_INPUT,ADD_INPUT,DELETE_INPUT } from './actionType'
export const changeInputAction = (value) = > ({
  type:CHANGE_INPUT,
  value
})
export const addInputAction = () = > ({
  type:ADD_INPUT,
})
export const deleteInputAction = (index) = > ({
  type:DELETE_INPUT,
  index
})
Copy the code

Method code for modifying component pages:

 onChangeValue(e) {
    console.log(e.target.value)
    const action = changeInputAction(e.target.value)
    store.dispatch(action)
  }
  addInput() {
    const action = addInputAction()
    store.dispatch(action)
  }
  deleteInput(index) {
    const action = deleteInputAction(index)
    store.dispatch(action)
  }
Copy the code

There are still some problems

A simple todolist is done here. Sort out the data flow in Redux a little bit. I’ve done a little filing, but I’m not done yet. Although the function is implemented correctly, we may need more in the project, such as involving the following questions:

  1. How does react-Redux simplify things?
  2. I use deep copy to create new data in reducer, but deep copy itself is performance-consuming. How to solve this problem? How to achieve immutable data?
  3. I only have one component page above, so I only use one Reducer. What if there are multiple components? How is state managed, how is Redux resolved, and how can we design directory structures to be better managed?
  4. When using axios data request? How are asynchronous operations handled, and how is redux-thunk used? .

There are a few other points that we can take to the next step:

React-redux +redux-thunk How to handle data streams

In the todolist mini-project above, I think you got a general idea of how to use Redux. In this step, I’ll spend more time on structured modularity and some new knowledge about using React-Redux.

Here are some apis or knowledge points to use, a little introduction, the use of the following will be clearer:

1. Introduction of knowledge points involved

  • react-redux

When using React-Redux, components fall into two broad categories, UI (responsible for UI rendering) and container components (responsible for managing data and logic)

  • Connect

React-redux provides a method for generating container components from UI components.

Why should it be generated from UI components? Because if we have a component that has both UI and logic, then we’re going to split it up into an outer layer where the container component takes care of the logic, just passes the data to it, and the inner layer is the UI component that takes the data from the outer layer and does the view rendering.

The connect method takes two parameters mapStateToProps: MapStateToProps (props) is used to mapStateToProps (props). If state is not updated to the Store, the mapStateToProps will automatically recalcitate the parameters of the UI component, triggering the re-rendering of the UI component. MapDispatchToProps: 1. Is responsible for output logic that maps user actions to UI components as actions. 2

  • Provider
<Provider store={store}>
	<App />
<Provider/>
Copy the code

When we generate the container component using connect, we need to get the component to hold the state object to generate the UI component’s parameters. Select a layer outside the root component so that the component gets state by default

  • redux-thunk

To implement asynchronous middleware, write an Action Creator that returns functions, and then modify store.dispatch using redux-Thunk middleware (more on this below).

  • immutable

Persistent data, once created, does not change. Modifying an IMMUTABLE object returns a new IMmutable object, but the original data does not change. We know that data cannot be directly changed in reducer, and immutable can be used here

2. Main process

  1. Under port 3001, there is a front-end project react, and under port 3000, there is an interface (built with express, do not spray. Just for effect), how does the front end make requests to the back end to display data to the page?
  2. Where do you put the data request? In the component? Or is it under the unified management of Store?
  3. An AXIos request involves asynchrony. How to write an asynchronous Action?

All right, no more talking. Let’s get started

Axios redux redux redux-thunk immutable

Start with the project structure:

  1. The API is responsible for data requests
  2. Pages manages components, each with its own Store folder
  3. Store This is a global store that controls the state, and reducer of all components

So let's say we're doing a shoplist display of commodity information, and it's empty at first, and then I need to request his information from the back end, and change the value of the shoplist

2.1 Data Request:

  • Story – thunk use:
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk' // Asynchronous processing
import reducer from './reducer'
const store = createStore(reducer,applyMiddleware(thunk))
export default store
Copy the code
  • api/config.js
// Encapsulate the axiox request
import axios from 'axios';
//1. Set a unified address range
export const baseUrl = 'http://localhost:3000/shop';
// return an axios instance
const axiosInstance = axios.create({
  baseURL:baseUrl
});
export {
  axiosInstance
}
Copy the code

api/request.js

import {axiosInstance} from "./config";
// All requests are managed here
//axios 
// What if the url is changed?
/ / home page
export const getShopListRequest = id= > {
  return axiosInstance.get('/shop')}Copy the code

2.2. Trigger action:

When dealing with asynchronous requests, we need to design two actions, one to change the value of the action synchronously, and one to send the request and return a function that also has the dispatch capability.

export const shopList = data= > ({
  type:SHOP_LIST,
  data
})
// API two action axios only in action
export const getShopList = () = > {
  return dispatch= > {
    getShopListRequest()
    .then(res= > {
      console.log(res) dispatch(shopList(res)); }}})Copy the code

2.3 Receiving and processing of components:

Using Provider in app.js:

<Provider store={store}>
    <Shop />
</Provider>
Copy the code

Use connect in the Shop component:

const Shop = (props) = > {
    const { shoplist,shopListDispatch } = props
    console.log(shoplist)
    useEffect(() = >{ shopListDispatch(); }, [])return(
        <div></div>)}const mapStateToProps = state= > ({
    shoplist: state.getIn(["shoplist"])})const mapDispatchToProps = dispatch= > {
    return{
        shopListDispatch() {
          dispatch(getShopList())
        },
    }
}
export default connect(mapStateToProps,mapDispatchToProps)(Shop);
Copy the code

2.4 deal with and integrate reducer

  • Reducer Actions:
const defaultStatus=fromJS({
   shopList:[]
})
export default (state=defaultStatus,action)=>{
    switch(action){
        case actionType.SHOP_LIST:
            return state.set('shopList',action.data);
        default:
            return state
    }
}
Copy the code
  • How to integrate multiple Reducer:
import {combineReducers} from 'redux-immutable'  // Immutable state
import {reducer as userReducer} from '.. /pages/Shop/store'
// import {reducer as rankReducer} from '.. /pages/Food/store'

export default combineReducers({
    shop:shopReducer,
    // food:foodReducer
})
Copy the code

Here are a few steps :(not all)

  1. The initial Shoplist data is empty
  2. In actionCreators. Js, two actions are created, one synchronous and one asynchronous
  3. The asynchronous action returns a function that takes a dispatch argument and executes the getShopListRequest() method that initiates the asynchronous request.
  4. Then we by requesting http://localhost:3000/shop/shop, obtained the following data:

5. Actions can be received in reducer. Js and we can print our actions:

conclusion

To sum up:

In the previous todolist project, we made several points, and here are some answers:

  • React-redux uses Provider and Connect to separate components into container components and UI components. The first parameter of Connect implements subscription for us, so we don’t have to write it ourselvesStatus of subscribing to Redux.
  • Reducer we did not use deep copy but immutable data. We need to know some apis like.fromjs,.setin,.getin…
  • In order to better manage the data of several components, the Reducer of components is integrated through combineReducers.
  • Asynchronous requests are processed by writing logic inside actionCreators. Instead of simply returning an action object. (Before you can do that, of course, you need to reference a wave from where you created the store.)

Personally, the first Todolist mini-project is a good example of how redux data flows and is easy to understand. However, it’s not always that simple in a project. The Provider Connect used in React-Redux may be more suitable for complex projects, but it also requires some cost. But now a lot of people use this. So we should also understand and be familiar with their use.

The most important intermediate directory structure, WHICH I don’t think I’ve made very clear, maybe you can take a look at a project you’re working on, which involves redux management and probably should be considered.

Ok, redux, I sort out here, maybe it is a little complicated, because the whole process is mostly based on the project.

  • Finally, the implementation of their own small white interface:

Because the back-end front-end in :3000,:3001, respectively, in the middle of some simple solution to cross domain code.

server.js:

const express = require('express');
const shopRouter = require('./shop');

const app = express();

app.all(The '*'.(req, res, next) = > {
  res.header("Access-Control-Allow-Origin"."*");
  res.header("Access-Control-Allow-Headers"."X-Requested-With");
  res.header("Access-Control-Allow-Methods"."PUT,POST,GET,DELETE,OPTIONS");
  res.header("X-Powered-By".'Express');
  res.header("Content-Type"."application/json; charset=utf-8");
  next();
});


app.use('/shop',shopRouter);

app.listen(3000.() = > {
  `server is running at port 8000 success`
})

Copy the code

shop.js:

const express = require('express')

const Router = express.Router();

Router.get('/shop'.(req,res) = > {
  return res.json(
    {shoplist: [{id:111.name:'cloth1'.price:123
    },
    {
      id:112.name:'cloth2'.price:345}]})})module.exports = Router
Copy the code

My name is Jingda, a junior, and I am preparing for the internship interview.

This period of time should be mainly to clarify some knowledge points, welcome to study together. Wx: lj18379991972 💕 💕 💕

Your likes are my biggest support 🤞

(After that part, I feel like I understand is not very good, if you are the big guy, trouble to see the wrong place to show me this cabbage. If you don’t know much about it, I hope this article will help you know what you might need to know. Maybe IT is because I have little practice and lack of depth and breadth to the problem. Will continue to learn.

Reference Documents:

Technology fat Redux free video

Ruan Yifeng Redux tutorial

Redux Chinese document