“Redux” concept understanding + hands-on combat (including a large number of examples)

Time:2021-7-19

preface

I can also read this article on a zero basis. I try my best to make it simple and easy to understand. If I think it’s a little hard to understand, I can go to the official website first.

This article also attached a lot of actual combat code, but due to the length, some actual combat examples I did not directly put out, but put in the sandbox link, in addition to slow, there are still many advantages.

This article is released by the lion team. WeChat official account: lion lion front-end, welcome everyone’s attention.

1、 Why did Redux come into being?

First, why does it appear

1. Trend: Javascript single page application development is becoming more and more complex,JavaScript needs to manage more states than ever before.

2. It is very difficult to manage the changing state: if the change of one model will cause the change of another model, then when the view changes, it may cause the change of the corresponding model and another model. In turn, it may cause the change of another view.When, for what reason, and how the state changes are out of control.

2、 What does Redux do?

After all, it’s just a tool. To understand a tool, of course, we need to understand what it does at the beginning.

The official website defines it as: Redux is a JavaScript state container, which provides predictable state management.

More details:

  • Redux will store the entire application state (in fact, data) in theStore
  • Store a status tree(state tree)
  • The only way a component can change a state is by calling storedispatchMethod to trigger aactionThis action is calledreducerThen the state completes the update
  • Components can dispatch actions to the store instead of directly notifying other components
  • Other components can refresh their views by subscribing to the state in the store

You can see from this picture:

3、 How to use Redux?

Official website example (todo)

State:A common object is used to describe the state in the application, and there is no setter (modifier method)

{
  todos: [{
    text: 'Eat food',
    completed: true
  }, {
    text: 'Exercise',
    completed: false
  }],
  visibilityFilter: 'SHOW_COMPLETED'
}

Action:To update the data in the state, such as adding todo, you need to initiate an action. An action is a normal JavaScript object, an indicator of what happened

{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }

The advantage of forcing action to describe all changes is that you can clearly know what’s going on in the application. If something changes, you can know why.

Reducer:String action and state. Reducer is just a function that receives state and action and returns a new state.

//Write many small functions to manage part of the state separately
function visibilityFilter(state = 'SHOW_ALL', action) {
  if (action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter;
  } else {
    return state;
  }
}

function todos(state = [], action) {
  switch (action.type) {
  case 'ADD_TODO':
    return state.concat([{ text: action.text, completed: false }]);
  case 'TOGGLE_TODO':
    return state.map((todo, index) =>
      action.index === index ?
        { text: todo.text, completed: !todo.completed } :
        todo
   )
  default:
    return state;
  }
}

``````
//The reducers call the two reducers above to manage the state of the whole application
function todoApp(state = {}, action) {
  return {
    todos: todos(state.todos, action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action)
  };
}

Handwritten combat (todolist)

Interested can have a lookCodesandbox todolist exampleIt may be slow.

If you haven’t used sandbox, I’ll show you what it looks like

I think this is more intuitive, so some of the links are still posted
Todo is a relatively simple example, which is equivalent to getting started and understanding the work of redux.

4、 React Redux

As you can see, we didn’t use react Redux above. Although we can realize the function, we will find that I take the store directly. If there are many components, we all take the store. This is not good. Let me summarize the problems that you may encounter without react Redux, such as:

1. The store is not so obvious. Once the component level becomes more complex, the store will become very difficult to control.

2. The logic component looks very messy. The reason is not clear. State and dispatch are not written together. There are too many duplicate codes, which is not intuitive.

3. It is not convenient for react component to read data from Redux store and distribute actions update data to store.

Provider

This is very easy to understand, that is, to directly integrate the store into the top props of the react applicationProvider injects the store into the context, and connect converts the context into propsThe advantage is that all components can be under the control of react Redux, and all components can access the data in redux.

<Provider store={store}>
    <App />
 </Provider>,

connect

  • Technically speaking, the container component uses store. Subscribe() to read part of the data from the Redux state tree and provide the data to the component to be rendered through props.
  • Why should we use it? Simply put, it saves work. We don’t have to manually develop container components and implement the shouldcomponentupdate method in the response performance optimization proposal for the sake of performance.
  • Use the connect () method of react Redux library to generate. This method optimizes the performance to avoid many unnecessary repeated rendering.

Use of connect

The code is as follows:

const App = connect(mapStateToProps, mapDispatchToProps)(Counter);
export default App;

mapStateToProps

Understand the word mapstatetoprops: map state to props, state is Redux state, props is react props.

code:

// Map Redux state to component props
function mapStateToProps(state) {
  return {
    value: state.count
  }
}

Then use this. Props. Value in the component to finish rendering

class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props;
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    );
  }
}
export default Counter;

mapDispatchToProps

Understand the word mapdispatchtoprops: map all kinds of dispatches become props.

// Map Redux actions to component props
 function mapDispatchToProps(dispatch) {
   return {
     onIncreaseClick: () => dispatch(increaseAction)
   }
 }

``````
class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props;
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    );
  }
}
export default Counter;

Similarly, you can call dispatch through this.props.onincreaseclick, so you don’t need to run dispatch in code.

Application examples of connect and provider

After reading the above introduction, you should be able to have a clearer understanding of what connect is for, and then you can basically understand how to do it, but there is still no example to make it clearer and more straightforward

Simply click to add an instance of countThere should be many areas that need to be optimized. Just learn to understand connect and provider.

A more complex instance of todolistHere we use hooks, connect and provider instead of hooks in react Redux (if you can’t understand it, you can learn to learn hooks or rewrite a class into hooks when I have time, it’s still very simple, as long as you concentrate on it)

5、 Redux under hooks

If you use hooks for project development, that’s good. You save a lot of effort, such as counterssimpleState management example, a few lines of code to solve.

import { useState } from 'react';
function Example() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

But can we not use Redux state management at all? Hahaha, how can it be

  • For those who have used Redux: first of all, before Redux gives better support for hooks, most of them will not completely refactor projects for hooks. By the way, the problems that refactoring may cause are as follows:

    • Losing a lot of the automatic reference cache provided by connect() can cause performance problems unless wrapped with a lot of usecallback()
    • If the code relies on ownprops in mapstatetoprops, you may use Redux hooks to write more code instead of getting this property directly.
    • It is not possible to provide dependency injection for action creator in mapdispatchtoprops as before
  • For complex applications: most of the projects of many companies use Redux to manage state. Many of its advantages, such as single data source, data sharing, transaction state, data state I / O and side effect isolation, state backtracking and powerful debugging ability brought by a series of auxiliary tools, make it a better choice to use Redux to manage data flow.
  • React Redux has released a new version, which is separated from the previous context API and provides support for hooks

Changes brought by new Redux

  1. No longer needed mapStateToPropsmapDispatchToPropsandconnectTo maintain a separate container component and UI component, you can directly use the hooks provided by Redux in the component to read the state in redux.
  2. You can customize any existingIntegrating hooks with ReduxInstead of passing the state created through hooks as a parameter to other hooks.

Redux support for hooks

First of all, we introduce several core concepts

  • useSelector:It is used to extract values from the state stored in Redux and subscribe to the state.
  • useDispatch:In addition to reading the state in the store, it can also update the state in the store with dispatch actions.
  • useStore:Used to get the created store instance.

It’s not very clear just from the introduction

useSelector

It’s like mapstatetoprops, but

  • Ownprops API is not provided. It’s better to use usecallback or usememo to get it
  • Like useeffect, if a second parameter is not provided, it will be recalculated every time the component is updated

There may be some worries. Will the new mapstatetoprops be easier to use than the mapstatetoprops used before? Let’s take a look at some of his benefits:

  • Of course, it’s easier to write code with hooksconcise
  • In terms of performance, it continues the previous performance optimization logic of redux,Compare propsIf the current props are the same as the old ones, the component will not be re rendered.
  • Batch update, so that multiple useselectors () recalculate the state, and the component will render again only once,Don’t worry about repeated rendering with useselector

First of all, let’s see how it used to be written:

//before

// React component
class Counter extends Component {
  render() {
    const { value, onIncreaseClick } = this.props;
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    );
  }
}
export default Counter;

// Connected Component
// Map Redux state to component props
function mapStateToProps(state) {return {value: state.count}}
// Map Redux actions to component props
function mapDispatchToProps(dispatch) {return {onIncreaseClick: () => dispatch(increaseAction)}}

// Connected Component
const App = connect(mapStateToProps,mapDispatchToProps)(Counter)
export default App

Then let’s rewrite the counter with a new useselect:

//after
const Counter = props=> {
  const { count } = useSelector(
    (state) => ({
      count: state.count
    })
  );
  return (
    <div>
      <span>{count}</span>
    </div>
  );
}
export default Counter;

useDispatch

Previously, mapdispatchtoprops was used:

//before
// Map Redux actions to component props
  function mapDispatchToProps(dispatch) {
    return {
      onIncreaseClick: () => dispatch(increaseAction)
    }
  }

Now using usedispatch, you can use it directly in the component in the form of anonymous function:

//after
const dispatch = useDispatch();
return (
    <div>
      <button onClick={()=>dispatch(increaseAction)}>Increase</button>
    </div>
  );

Because of the nature of anonymous functions, new references are obtained every time they are re rendered. If they are passed as props to the subcomponent, the subcomponent will be re rendered every time.

The optimization idea is to create this anonymous function in usecallback

//after
import React, { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import increaseAction from "./store/action";
const Counter = props=> {
  const { count } = useSelector(
    (state) => ({
      count: state.count
    })
  );
  const dispatch = useDispatch();
  const onIncreaseClick = useCallback(
    () => dispatch(increaseAction),[dispatch]
  );
  return (
    <div>
      <span>{count}</span>
      <button onClick={onIncreaseClick}>Increase</button>
    </div>
  );
}
export default Counter;

useStore

In any application that needs to access the store, it can be obtained through the usestore. If for some reason, such as unit testing, we want to obtain different stores, we can pass the store into the component tree through the new context API, as follows:

import React from 'react';
import { useStore } from 'react-redux';
import OtherProvider from './OtherProvider';

const Component = props => {
  const store = useStore();
  return <OtherProvider store={store}>{props.children}</OtherProvider>
}

actual combat

Then, the above has been changed to todolist of hooks, but the instance of connect is still used to re implement it with useselector and usedispatch of react redux.

The basic idea is almost the same as the previous introduction. Here I will use sandbox to make the code more intuitive, although it is not very fast

Sandbox — useselector, usedispatch, todolist

A summary of Redux in hooks

Why use Redux?
In short: Redux provides the ability to organize and debug code for large-scale applications, which can help you quickly locate problems in case of program errors.

For some scenarios, hooks can’t solve the problem

  • Need to save or load state
  • Sharing state across components
  • You need to share business logic or data processing with other components

The new Redux with hooks brings different changes: using useselector, usedispatch and usestore with hooks is really a good attempt.

summary

As a rookie from Vue technology stack to react technology stack, he has stepped on some pits
For example, after having the foundation of vuex, if I have a clear understanding of Redux, it’s easy to think that it’s almost the same, but actually there are many differences, which is also a fuse for my in-depth study of redux.

To put it simply:
In vuex, $store is directly injected into the component instance, so it can be used flexibly

  • Using dispatch and commit to submit updates
  • Read data through mapstate or directly through this. $store
  • In the component, you can either dispatch action or commit updates

In Redux:

  • Each of our components needs to be displayed. Connect the required props and dispatches with connect.
  • In Redux, you can only dispatch, not directly call reducer to modify.
    In terms of implementation principle, the biggest differences are two points

Redux uses immutable data, while vuex uses variable data. Redux replaces the old state with the new one every time, while vuex modifies it directly.

When Redux detects data changes, it compares the differences by diff, while vuex and Vue actually use getter / setter to compare.

Dig a hole

This article may be updated in the future, but some have not been written. I hope this article will be helpful for you to learn redux

⭐ ️ ⭐ If you think it’s good, please give me a compliment before you leave ❤️ ❤️ ~~