17【redux】

17 【redux】

introduction

We are now starting to learn Redux. In the cases we wrote before, our state management is realized through state. For example, when we pass data to sibling components, we need to pass the data to the parent component first, and then Forwarded by a parent component to its child components. This process is very complicated. Later, we learned about publishing and subscribing of messages . Through the pubsub library, we realized the forwarding of messages, directly published data, and subscribed by sibling components to realize data transfer between sibling components. However, as our needs continue to improve, we need to perform more complex data transfer and more levels of data exchange. So why can't we hand over all the data to a transfer station, which is independent of all components, and the transfer station distributes the data, so that no matter which component needs data, we can easily give it to He distributes.

And there is such a library that can help us achieve it, that is Redux, which can help us achieve centralized state management

1 Introduction

Redux – Li Lichao | lilichao.com

Teacher Li Lichao's explanation

A Predictable State Container for JS Apps is Redux's official description of Redux. This sentence can be translated as "a predictable state container designed for JS applications". Simply put, Redux is a predictable state container. What is it? ? These words can be recognized individually, but why are they not human-like when they are connected together? Don't worry, let's take a look at it bit by bit.

1.1 State (State)

The literal translation of state is state. We have been using React for so long, and we are already very familiar with state. state is just a variable, a variable used to record (component) state. Components can be switched to different displays according to different state values. For example, the pages seen by users who are logged in and not logged in should be different, so whether the user is logged in or not should be a state. For another example, whether the data is loaded or not, the displayed interface should also be different, so the data itself is a state. In other words, the state controls how the page is displayed.

But it is important to note that state is not unique to React or other similar frameworks. All programming languages ​​have states, and all programming languages ​​will execute different logic according to different states, which is certain. So what is the state, the state is a variable to record the execution of the program.

1.2 Containers

Of course, the container is used to hold things, and the state container is the container used to store the state. If there are too many states, we naturally need something to store them, but the function of the container is not just to store the state, it is actually a state manager. In addition to storing the state, it can also be used to query and modify the state. . (Containers in programming languages ​​almost always mean this, and their function is nothing more than adding, deleting, modifying and checking something)

1.3 Predictable

Predictability means that when we perform various operations on the state, the result is certain. That is, performing the same operations on states in the same order will give the same result. To put it simply, all operations on the state in Redux are encapsulated inside the container, and the outside can only manipulate the state by calling the methods provided by the container, but cannot directly modify the state. This means that external operations on the state are restricted by the container, and operations on the state are under the control of the container, that is, predictable.

Overall, Redux is a stable and safe state manager .

2. Why Redux?

Q: No? Isn't there already state in React? Why make a Redux as a state manager?

Answer: The state can handle simple values, but it is not very convenient if the values ​​are more complex.

Q: Can useReducer be used for complex values?

Answer: Yes! But whether it is state or useReducer, it is still inconvenient to pass the state, and it is not convenient to pass from top to bottom layer by layer!

Question: Isn't there still context?

Answer: It is true that the use of context can solve the problem of state transfer, but it is still acceptable for simple data. If the data structure is too complex, the context will become extremely large and inconvenient to maintain.

Redux can be understood as a combination of reducer and context. Using Redux can manage complex state, and can conveniently share and transfer state between different components. Of course, the main usage scenario of Redux is still large-scale applications. The state in large-scale applications is more complicated. If you only use reducers and contexts, it is not so convenient to develop. At this time, a powerful state manager becomes particularly important. .

3. When to use Redux

First of all, let's clarify the role Reduxof to achieve centralized state management.

ReduxIt is suitable for scenarios with multiple interactions and multiple data sources. simple to understand is complex

From the perspective of components, when we have the following application scenarios, we can try to use Reduxto achieve

  1. When the state of a component needs to be shared
  2. When a component needs to change the state of other components
  3. When a component needs to change the global state

In addition, there are many situations that need to be implemented using Redux

image-20221030202337038

As shown in the figure above, reduxby stateconcentrating on the top of the component, it is possible to flexibly statedistribute all the components as needed.

reduxThe three principles of:

  • The entire stateapplication is stored object treein a , and object treeonly exists storein the only (this does not mean that all the s need reduxto statestored reduxin the , and the component can still maintain itself state).
  • stateis read-only. stateA change in , will result in viewa change in the view ( ). The user can't touch it state, only the view, and the only way stateto change is to trigger it in the view action. actionis a generic object used to describe an event that has occurred.
  • Use reducersto perform statean update of . reducersIs a pure function that accepts actionand the current stateas parameters, and returns a new one through calculation state, so as to update the view.

4. The workflow of Redux

image-20221030202728807

As shown in the figure above, reduxthe workflow of the program is roughly as follows:

  • First, the user is emitted via store.dispatchthe method action.
  • Then, storeit is called automatically reducers, and it is passed two parameters: current stateand received action. reducerswill return new state.
  • Finally, when storelistening to the change stateof , the listening function will be called to trigger the re-rendering of the view.

5.Redux API

5.1 store

  • storeIt is the place where the data is saved, and there can only be one for the entire application store.
  • reduxcreateStoreThis function is provided to create a storeto hold the entire application state:
import {
    
     createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);

createStore is used to create a container object in Redux, which requires three parameters: reducer, preloadedState, enhancer.

  • reducerIt is a function, which is an integrated function of state operations. This function will be triggered every time the state is modified, and its return value will become the new state.

  • preloadedStateIt is the initial value of the state, which can be specified here or in the reducer.

  • enhancerThe enhancement function is used to extend the function of state, ignore it for now.

5.2 state

  • storeObjects contain all data. If you want to get the data at a certain point in time, you need to storegenerate a snapshot. This point-in-time data set is called state.
  • If you want to get the current moment state, you can get it through store.getState()the method :
import {
    
     createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);

const state = store.getState();

5.3 action

  • stateChanges in , will result in changes in the view. However, the user is not exposed state, only the view. Therefore, statethe change must be initiated by the view.
  • actionIt is the notification sent by the view that the shouldstore change at this time .state
  • actionis an object. The typeattribute is required and represents actionthe name of the . Other attributes can be set freely, and the community has a specification for reference:
const action = {
    
    
  type: 'ADD_TODO',
  payload: 'Learn Redux' // 可选属性
};

The above code defines a name ADD_TODOnamed action, and the data information it carries is Learn Redux.

5.4 Action Creator

  • viewThere will be as many kinds of messages as there are to be sent action, and it will be very troublesome if they are all handwritten.
  • You can define a function to generate action, this function is called Action Creator, as in the following code addTodofunction :
const ADD_TODO = '添加 TODO';

function addTodo(text) {
    
    
  return {
    
    
    type: ADD_TODO,
    text
  }
}

const action = addTodo('Learn Redux');
  • redux-actionsis a useful library that makes writing reduxstate management easy. The library provides createActionmethods for creating action creators:
import {
    
     createAction } from "redux-actions"

export const INCREMENT = 'INCREMENT'
export const increment = createAction(INCREMENT)
  • The above code defines an action INCREMENT, and then passescreateAction

    A correspondence is created Action Creator:

    • increment()will return when calling{ type: 'INCREMENT' }
    • call increment(10)returns{ type: 'INCREMENT', payload: 10 }

5.5 store.dispatch()

  • store.dispatch()is the only method that the view actionemits , which accepts a actionobject as a parameter:
import {
    
     createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);

store.dispatch({
    
    
  type: 'ADD_TODO',
  payload: 'Learn Redux'
});
  • Combined Action Creator, this code can be rewritten as follows:
import {
    
     createStore } from 'redux';
import {
    
     createAction } from "redux-actions"
const store = createStore(reducer, [preloadedState], enhancer);

const ADD_TODO = 'ADD_TODO';
const add_todo = createAction('ADD_TODO'); // 创建 Action Creator

store.dispatch(add_todo('Learn Redux'));

5.6 reducer

  • storeactionUpon receipt , a new one must be given statein order for the view to be updated. stateThe calculation (update) process of is reducerrealized .
  • reduceris a function that takes actionand the current stateas parameters and returns a new one state:
const reducer = function (state, action) {
    
    
  // ...
  return new_state;
};
  • In order to automatically execute the function when store.dispatchthe method , it is necessary to pass in the method whenreducer creating the :storereducercreateStore
import {
    
     createStore } from 'redux';
const reducer = function (state, action) {
    
    
  // ...
  return new_state;
};
const store = createStore(reducer);
  • In the code above, createStorethe method accepts reduceras a parameter and generates a new one store. In the future, whenever the view uses store.dispatchto send to storea new one action, reducerthe function to get the updated one state.
  • redux-actionsProvides handleActionsmethods for handling multiple action:
// 使用方法:
// handleActions(reducerMap, defaultState)

import {
    
     handleActions } from 'redux-actions';
const initialState = {
    
     
  counter: 0 
};

const reducer = handleActions(
  {
    
    
    INCREMENT: (state, action) => ({
    
    
      counter: state.counter + action.payload
    }),
    DECREMENT: (state, action) => ({
    
    
      counter: state.counter - action.payload
    })
  },
  initialState,
);

6. Use directly in the web page

Let's first use the following Redux in the webpage. Using Redux in the webpage is like using jQuery, just import the library file of Redux in the webpage directly:

<script src="https://unpkg.com/[email protected]/dist/redux.js"></script>

In the webpage, we implement a simple counter function, and the page looks like this:

image-20221030210318059

<button id="btn01">减少</button>
<span id="counter">1</span>
<button id="btn02">增加</button>

The function we want to achieve is very simple, click to decrease the number to become smaller, click to increase the number to become larger. If you write in traditional DOM, you can create a variable to record the quantity, click different buttons to modify the variable and set it in the span, the code looks like this:

Without using Redux:

const btn01 = document.getElementById('btn01');
const btn02 = document.getElementById('btn02');
const counterSpan = document.getElementById('counter');

let count = 1;

btn01.addEventListener('click', ()=>{
    
    
   count--;
   counterSpan.innerText = count;
});

btn02.addEventListener('click', ()=>{
    
    
   count++;
   counterSpan.innerText = count;
});

In the above code, count is a state, but there is no special manager for this state, and all its operations are processed in the event response function. This state is an unpredictable state, because it can be used in any function. The state is modified without any security restrictions.

Using Redux:

Redux is a state container, so to use Redux, you must first create a container object, and all its operations are performed through the container object

Redux.createStore(reducer, [preloadedState], [enhancer])

Among the three parameters, only the reducer is required. Let's look at an example of a reducer:

const countReducer = (state = {
     
     count:0}, action) => {
    
    
    switch (action.type){
    
    
        case 'ADD':
            return {
    
    count:state.count+1};
        case 'SUB':
            return {
    
    count:state.count-1};
        default:
            return state
    }
};

state = {count:0}This is the default value of the specified state. If not specified, the value of state will be undefined when called for the first time. You can also specify this value as the second parameter of createStore(). action is an ordinary object used to store operation information.

After passing the reducer into createStore, we will get a store object:

const store = Redux.createStore(countReducer);

After the store object is created, all operations on the state need to be performed through it:

read state:

store.getState()

Modify state:

store.dispatch({
    
    type:'ADD'})

The dipatch is used to trigger the operation of the state, which can be understood as a tool to send tasks to the reducer. It needs an object as a parameter, and this object will become the second parameter action of the reducer, and the operation information needs to be set in the object and passed to the reducer. The most important attribute in action is type, which is used to identify different operations on the state. In the above example, 'ADD' means an increase operation, and 'SUB' means a decrease operation.

In addition to these methods, the store also has a subscribe method, which is used to subscribe to state change information. This method requires a callback function as a parameter. When the state stored in the store changes, the callback function will be called automatically. We can define the operation to be triggered when the state changes in the callback function:

store.subscribe(()=>{
    
    
    // store中state发生变化时触发
    console.log('state变化了')
    console.log(store.getState())
    spanRef.current.innerText = store.getState().count
});

In this way, the code just now has been modified to look like this:

The revised code is a bit more complicated than the first version, but it also solves some problems in the previous code:

  1. The code state of the previous version is a variable that can be modified arbitrarily. The state is unpredictable and can easily be changed to wrong values. Redux is used in the new code. All operations on the state in Redux are encapsulated into the reducer function, which can limit the modification of the state and make the state predictable, effectively avoiding wrong state values.
  2. In the previous version of the code, every time you click the button to modify the state, you have to manually modify the innerText of the counterSpan, which is very troublesome. In this way, if we add new functions, we still cannot forget to modify it. In the new code, the modification of counterSpan is carried out in the callback function of store.subscribe(). Every time the state changes, its value will change accordingly, and there is no need to manually modify it. In other words, state and DOM elements are bound together through Redux.

It is not difficult to see from the above example that the most core thing in Redux is the store. As long as you get the store object, it is equivalent to getting the data stored in Redux. One of the core ideas of adding Redux is called "single data source", that is, all states will be stored in a class object tree, and this object tree will be stored in a store. So in React, components only need to get the store to get all the states stored in Redux.

7. Basic use of React-Redux

7.1 Introduction

We learned about Redux earlier. When we wrote the case, we also found some problems in it. For example, the status of the component cannot be shared, and each status component needs to be monitored through subscription. The status update will affect the update of all components. Faced with these problems, React officially proposed the React-Redux library based on redux

In the previous case, if we write the store directly in the top-level props of the React application, each sub-component can access the top-level props

<顶层组件 store={
    
    store}>
  <App />
</顶层组件/>

This is similar to React-Redux

7.2 Getting started with React-Redux

When we need to use Redux in React, in addition to introducing the Redux core library, we also need to introduce the react-redux library to adapt React and redux, which can be installed through npm or yarn:

npm install -S redux react-redux

Next, we try to add some complex state in Redux, such as a student's information:

{
    
    name:'孙悟空', age:18, gender:'男', address:'花果山'}

Create reducers:

const reducer = (state = {
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山'
}, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_AGE':
            return {
                ...state,
                age: action.payload
            };
        case 'SET_ADDRESS':
            return {
                ...state,
                address: action.payload
            };
        case 'SET_GENDER':
            return {
                ...state,
                gender: action.payload
            };
        default :
            return state
    }

};

There is no essential difference between the writing of the reducer and the previous case, but this time the data and operation methods have become more complicated. Take SET_NAME as an example, when the name attribute needs to be modified, dispatch needs to pass an action with two attributes, the type of the action should be the string "SET_NAME", and the payload should be the new name to be modified, for example, to change the name to Zhubajie , then dispatch needs to pass such an object {type:'SET_NAME',payload:'猪八戒'}.

Create store:

const store = createStore(reducer);

There is no difference between creating a store and the previous example, just pass the reducer to build.

7.3 Set provider

Since our state may be used by many components, React-Redux provides us with a Provider component that can be injected into the store in redux globally, just register the Provider in the root component

For example, when the following components all need to use the store, we need to do this, but this increases the workload and is very inconvenient

<Count store={
    
    store}/>
{
    
    /* 示例 */}
<Demo1 store={
    
    store}/>
<Demo2 store={
    
    store}/>
<Demo3 store={
    
    store}/>
<Demo4 store={
    
    store}/>
<Demo5 store={
    
    store}/>

We can do this: in main.jsxthe file , import Provider, wrap the component directly with Providerthe tag App, and storewrite Providerin

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <App />
  </Provider>
)

7.4 Manipulating data

access data:

const stu = useSelector(state => state);

react-redux also provides us with a hook function useSelector to obtain the data stored in Redux. It needs a callback function as a parameter. The first parameter of the callback function is the current state, and the return value of the callback function will be used as useSelector The return value of is returned, so state => stateit means that the entire state is returned directly as the return value. Now you can read the data in the state through stu:

<p>
    {
    
    stu.name} -- {
    
    stu.age} -- {
    
    stu.gender} -- {
    
    stu.address}
</p>

Operation data:

const dispatch = useDispatch();

useDispatch is also a hook function provided by react-redux, which is used to obtain the dispatcher of redux, and all operations on the state need to be performed through the dispatcher.

Modify the state through the dispatcher:

dispatch({
    
    type:'SET_NAME', payload:'猪八戒'})
dispatch({
    
    type:'SET_AGE', payload:28})
dispatch({
    
    type:'SET_GENDER', payload:'女'})
dispatch({
    
    type:'SET_ADDRESS', payload:'高老庄'})

Full code:

import ReactDOM from 'react-dom/client';
import {Provider, useDispatch, useSelector} from "react-redux";
import {createStore} from "redux";

const reducer = (state = {
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山'
}, action) => {
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.payload
            };
        case 'SET_AGE':
            return {
                ...state,
                age: action.payload
            };
        case 'SET_ADDRESS':
            return {
                ...state,
                address: action.payload
            };
        case 'SET_GENDER':
            return {
                ...state,
                gender: action.payload
            };
        default :
            return state
    }

};

const store = createStore(reducer);

const App = () =>{
    const stu = useSelector(state => state);
    const dispatch = useDispatch();
    return  <div>
        <p>
            {stu.name} -- {stu.age} -- {stu.gender} -- {stu.address}
        </p>
        <div>
            <button onClick={()=>{dispatch({type:'SET_NAME', payload:'猪八戒'})}}>改name</button>
            <button onClick={()=>{dispatch({type:'SET_AGE', payload:28})}}>改age</button>
            <button onClick={()=>{dispatch({type:'SET_GENDER', payload:'女'})}}>改gender</button>
            <button onClick={()=>{dispatch({type:'SET_ADDRESS', payload:'高老庄'})}}>改address</button>
        </div>
  </div>
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <App />
  </Provider>
)

8. Complex State

The data structure in the above example has become complicated, but there is still a certain gap from the real project. Because the core idea of ​​Redux is that all states should be stored in the same warehouse, so only one student data is indeed a bit thin. Now the data is made more complicated. In addition to the student data, a school information is added, so The structure of state becomes like this:

{
    
    
    stu:{
    
    
        name: '孙悟空',
        age: 18,
        gender: '男',
        address: '花果山' 
    },
    school:{
    
    
        name:'花果山一小',
        address:'花果山大街1号'
    }
}

The data structure has become complicated, we need to modify the code, first look at the reducer:

const reducer = (state = {
    
    
    stu: {
    
    
        name: '孙悟空',
        age: 18,
        gender: '男',
        address: '花果山'
    },
    school: {
    
    
        name: '花果山一小',
        address: '花果山大街1号'
    }

}, action) => {
    
    
    switch (action.type) {
    
    
        case 'SET_NAME':
            return {
    
    
                ...state,
                stu: {
    
    
                    ...state.stu,
                    name: action.payload
                }
            };
        case 'SET_AGE':
            return {
    
    
                ...state,
                stu: {
    
    
                    ...state.stu,
                    age: action.payload
                }
            };
        case 'SET_ADDRESS':
            return {
    
    
                ...state,
                stu: {
    
    
                    ...state.stu,
                    address: action.payload
                }
            };
        case 'SET_GENDER':
            return {
    
    
                ...state,
                stu: {
    
    
                    ...state.stu,
                    gender: action.payload
                }
            };
        case 'SET_SCHOOL_NAME':
            return {
    
    
                ...state,
                school: {
    
    
                    ...state.school,
                    name:action.payload
                }
            };
        case 'SET_SCHOOL_ADDRESS':
            return {
    
    
                ...state,
                school: {
    
    
                    ...state.school,
                    address: action.payload
                }
            }
        default :
            return state;
    }

};

The data level has increased, and our data manipulation has become more complicated. For example, the logic of modifying the name has become like this:

case 'SET_NAME':
    return {
    
    
         ...state,
        stu: {
    
    
            ...state.stu,
            name: action.payloadjs
    }
};

At the same time, the logic of data loading also needs to be modified. Before we returned the entire state, now we need to obtain the state according to different situations. For example, to obtain student information, write as follows:

const stu = useSelector(state => state.stu);

Get school information:

const school = useSelector(state => state.school);

Full code:

store/stuReducer.js

const stuReducer = (
  state = {
    
    
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山',
  },
  action,
) => {
    
    
  switch (action.type) {
    
    
    case 'SET_NAME':
      return {
    
    
        ...state,
        name: action.payload,
      }
    case 'SET_AGE':
      return {
    
    
        ...state,
        age: action.payload,
      }
    case 'SET_ADDRESS':
      return {
    
    
        ...state,
        address: action.payload,
      }
    case 'SET_GENDER':
      return {
    
    
        ...state,
        gender: action.payload,
      }
    default:
      return state
  }
}

export default stuReducer

store/index.js

import {
    
     createStore } from 'redux'
import stuReducer from './stuReducer.js'

const store = createStore(stuReducer)

export default store

main.js

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import {
    
     Provider } from 'react-redux'
import store from './store'

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={
    
    store}>
    <App />
  </Provider>,
)

App.jsx

import React from 'react'
import { useDispatch, useSelector } from 'react-redux'

export default function App() {
  const stu = useSelector(state => state)
  const dispatch = useDispatch()
  return (
    <div>
      <p>
        {stu.name} -- {stu.age} -- {stu.gender} -- {stu.address}
      </p>
      <div>
        <button
          onClick={() => {
            dispatch({ type: 'SET_NAME', payload: '猪八戒' })
          }}
        >
          改name
        </button>
        <button
          onClick={() => {
            dispatch({ type: 'SET_AGE', payload: 28 })
          }}
        >
          改age
        </button>
        <button
          onClick={() => {
            dispatch({ type: 'SET_GENDER', payload: '女' })
          }}
        >
          改gender
        </button>
        <button
          onClick={() => {
            dispatch({ type: 'SET_ADDRESS', payload: '高老庄' })
          }}
        >
          改address
        </button>
      </div>
    </div>
  )
}

The trouble is indeed a little troublesome, but fortunately the function has been realized.

9. Multiple Reducers

There is a very serious problem with the writing of the above case! Writing all the code into a reducer will make the reducer extremely large, and now there are only two information about students and schools. If there is more data, the number of operation methods will also increase, and the reducer will become larger and more difficult to maintain.

Redux allows us to create multiple reducers, so we can split the reducer in the above example into two reducers according to its data and functions, like this:

const stuReducer = (state = {
    
    
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山'
}, action) => {
    
    
    switch (action.type) {
    
    
        case 'SET_NAME':
            return {
    
    
                ...state,
                name: action.payload
            };
        case 'SET_AGE':
            return {
    
    
                ...state,
                age: action.payload
            };
        case 'SET_ADDRESS':
            return {
    
    
                ...state,
                address: action.payload
            };
        case 'SET_GENDER':
            return {
    
    
                ...state,
                gender: action.payload
            };
        default :
            return state;
    }

};

const schoolReducer = (state = {
    
    

    name: '花果山一小',
    address: '花果山大街1号'

}, action) => {
    
    
    switch (action.type) {
    
    
        case 'SET_SCHOOL_NAME':
            return {
    
    
                ...state,
                name: action.payload
            };
        case 'SET_SCHOOL_ADDRESS':
            return {
    
    
                ...state,
                address: action.payload
            };
        default :
            return state;
    }

};

After the modification, the reducer is split into stuReducer and schoolReducer. After splitting, when writing each reducer, only the current state data needs to be considered, and operations such as copying irrelevant data are no longer required, which simplifies the writing of reducers. At the same time, different functions are written into different reducers, which reduces the coupling between codes and facilitates code maintenance.

After splitting, we need to use the function combineReducer provided by Redux to combine multiple reducers, and then pass them into createStore to create a store.

const reducer = combineReducers({
    
    
    stu:stuReducer,
    school:schoolReducer
});

const store = createStore(reducer);

combineReducer requires an object as a parameter, and the attribute name of the object can be specified as needed. For example, if we have two kinds of data stu and school, the attribute names are named stu and school, stu points to stuReducer, and school points to schoolReducer. When reading data, directly read student data through state.stu, and read school data through state.school

Full code:

store/reducers.js

export const stuReducer = (
  state = {
    
    
    name: '孙悟空',
    age: 18,
    gender: '男',
    address: '花果山',
  },
  action,
) => {
    
    
  switch (action.type) {
    
    
    case 'SET_NAME':
      return {
    
    
        ...state,
        name: action.payload,
      }
    case 'SET_AGE':
      return {
    
    
        ...state,
        age: action.payload,
      }
    case 'SET_ADDRESS':
      return {
    
    
        ...state,
        address: action.payload,
      }
    case 'SET_GENDER':
      return {
    
    
        ...state,
        gender: action.payload,
      }
    default:
      return state
  }
}

export const schoolReducer = (
  state = {
    
    
    name: '花果山一小',
    address: '花果山大街1号',
  },
  action,
) => {
    
    
  switch (action.type) {
    
    
    case 'SET_SCHOOL_NAME':
      return {
    
    
        ...state,
        name: action.payload,
      }
    case 'SET_SCHOOL_ADDRESS':
      return {
    
    
        ...state,
        address: action.payload,
      }
    default:
      return state
  }
}

store/index.js

import {
    
     createStore, combineReducers } from 'redux'
import {
    
     stuReducer, schoolReducer } from './reduces.js'

const reducer = combineReducers({
    
    
  stu: stuReducer,
  school: schoolReducer,
})

const store = createStore(reducer)

export default store

main.js

8.复杂的Statesame as

App.jsx

import React from 'react'
import { useDispatch, useSelector } from 'react-redux'

export default function App() {
  const stu = useSelector(state => state.stu)
  const school = useSelector(state => state.school)
  const dispatch = useDispatch()
  
  return (
    <div>
      <p>
        {stu.name} -- {stu.age} -- {stu.gender} -- {stu.address}
      </p>
      <div>
        <button
          onClick={() => {
            dispatch({ type: 'SET_NAME', payload: '猪八戒' })
          }}
        >
          改name
        </button>
        <button
          onClick={() => {
            dispatch({ type: 'SET_AGE', payload: 28 })
          }}
        >
          改age
        </button>
        <button
          onClick={() => {
            dispatch({ type: 'SET_GENDER', payload: '女' })
          }}
        >
          改gender
        </button>
        <button
          onClick={() => {
            dispatch({ type: 'SET_ADDRESS', payload: '高老庄' })
          }}
        >
          改address
        </button>
      </div>

      <hr />

      <p>
        {school.name} -- {school.address}
      </p>
      <div>
        <button
          onClick={() => {
            dispatch({ type: 'SET_SCHOOL_NAME', payload: '高老庄小学' })
          }}
        >
          改学校name
        </button>
        <button
          onClick={() => {
            dispatch({ type: 'SET_SCHOOL_ADDRESS', payload: '高老庄中心大街15号' })
          }}
        >
          改学校address
        </button>
      </div>
    </div>
  )
}

10. Summary

[External link image transfer...(img-KzjcqajQ-1669180737560)]

Redux does have a lot of new terms and concepts to remember. As a reminder, here's what we just covered:

  • Redux is a library for managing global application state
    • Redux is often used with the React-Redux library to integrate Redux and React together
    • Redux Toolkit is the recommended way to write Redux logic
  • Redux uses "one-way data flow"
    • State describes the state of the application at a certain point in time, and the view is rendered based on the state
    • When something happens in the application:
      • The view dispatches an action
      • The store calls the reducer, which then updates the state based on what happened
      • The store notifies the UI of state changes
    • The view re-renders based on the new state
  • Redux has these types of code
    • Action is a plain object with typea field describing what happened
    • Reducer is a pure function, based on the previous state and action to calculate the new state
    • Whenever an action is dispatched, the store will call the root reducer

Guess you like

Origin blog.csdn.net/DSelegent/article/details/127998462