React-Hooks and React-Redux

Note: Reference for the latest usage of Redux Personal React Column React Elementary Learning

Basic introduction to Hooks————————-

  • Hooks: hook, fishhook, hook. Hook is a special function that allows you to obtain status and other React features in function components. It is new in React v16.8 Added functionality

  • Role: Provide Function Component with React functions such as status, life cycle, etc. provided in the original class component

    • It can be understood as the feature of hooking class components into function components through Hooks

  • Note: Hooks can only be used in function components. Since then, function components have become the new favorite of React

Comparison of component development models before and after React v16.8:

  • Before React v16.8: class component (providing status) + function component (displaying content)

  • React v16.8 and later:

    1. class component (providing status) + function component (displaying content)

    2. Hooks (providing status) + function components (displaying content)

    3. Mix the above two methods: use class components for some functions, and use Hooks + function components for some functions.

Summary:

Note 1: Although Hooks are available, React officially has no plans to remove class from the React library

Note 2: With Hooks, Function components can no longer be called stateless components, because Hooks provide state for function components.

Why Hooks

  • Component state logic reuse
    • Before Hooks, the state logic reuse of components went through: mixins (mixing), HOCs (high-order components), render-props and other modes

    • (Long abandoned) Problems with mixins: 1. Unclear data sources 2. Naming conflicts

    • Problems with HOCs and render-props: Refactoring component structures causes components to form JSX nested hell problems

  • Problems with class components themselves
    • Choice: The difference between function and class components and which one is more appropriate to use

    • Need to understand how this in class works

    • Code that is related to each other and needs to be modified is split into different life cycle functions

    • Compared with function components, it is not conducive to code compression and optimization, and is not conducive to TS type derivation.

Notice:

The previous react syntax does not need to be used in the future. The API related to class components does not need to be used in hooks.

  • class’s own syntax, such as constructor, static, etc.
  • Hook functions, componentDidMount, componentDidUpdate, componentWillUnmount
  • this related usage

useState-Hooks

useState-basic use

  • useState functions: provides state for function components and cannot be called in class components.

  • useState Usage scenarios: When you want to use component state in a function component, you need to use useState Hook

  • Convention: The function that modifies the state name begins with set, followed by the name of the state

  • Calling useState multiple times will not affect each other between multiple states and functions that modify the state.

  • The state provided by useState is a local variable inside the function and can be used anywhere within the function.

  • Every time you render, useState gets the latest state value (react will remember the latest state value). The initial value (parameter) of useState will only take effect when the component is rendered for the first time.

grammar:

import { useState } from 'react'

// Parameters: The initial value of the state can be any value
//Return value: stateArray is an array
const stateArray = useState(0)

// Index 0 means: state value (state)
const state = stateArray[0]
// Index 1 represents: a function that modifies the state (setState(newValue)` is a function, and the parameters represent: *new state value*)
const setState = stateArray[1]

Reading and modifying status:

  • read status

const Counter = () => {
const [user, setUser] = useState({ name: ‘jack’, age: 18 })

return (

Name: {user.name}

Age: {user.age}

)
}

  • Modify status
    • When this function is called, the old value is replaced with the new state value
    • After modifying the state, the component will be re-rendered because the state has changed.
    • setUser(newValue) is a function whose parameters represent: new status value. When this function is called, the new state value will overwrite the original state value. Therefore, this is different from class’s setState. To distinguish it, class’s setState will pass whichever value needs to be modified. , the state will be merged internally. When hooks modify the state value, it will be directly overwritten and not merged. Therefore, when hooks modify the state value, the original value needs to be deconstructed first and then modified.
const Counter = () => {
  //Use array destructuring to extract status and modify status
  const [user, setUser] = useState({ name: 'jack', age: 18 })
  
  const onAgeAdd = () => {
    setUser({
      ...user,
      age: user.age + 1
    })
  }
  
  return (
  <div>
    <p>Name: {user.name}</p>
<p>Age: {user.age}</p>
     <button onClick={onAgeAdd}>Age + 1</button>
    </div>
  )
}

useState-use rules

  • How to provide multiple states to a function component?

    • Just call useState Hook multiple times. Each time useState Hook is called, it can provide a state.

    • useState Hook The [state, setState] returned by multiple calls does not affect each other.

  • Rules for using Hooks such as useState:

    • React Hooks can only appear directly in function components

    • React Hooks cannot be nested in if/for/other functions

    • Principle: React identifies each Hook according to the order in which Hooks are called. If the order of each call is different, React cannot know which Hook it is.

useEffect-Hooks

useEffect-basic meaning

  • Function: Handling side effects in function components
  • Side effects are relative to the main function. In addition to the main function, the other functions of a function (for example, function) are side effects. For React components, the main function is to render UI based on data (state/props), everything else is a side effect (for example, manually modifying the DOM)
  • Common side effects: data (Ajax) requests, manual modification of DOM, localStorage, console.log operations, etc.
  • The Complete Guide to useEffect: The Complete Guide to useEffect – Overreacted

useEffect-Basic use

  • Time to use

import { useEffect } from 'react'

// 1 
// Trigger timing: It will be executed for the first rendering, and it will be executed again every time the component is re-rendered.
// componentDidMount + ComponentDidUpdate
useEffect(() => {})

// 2 (most frequently used)
//Trigger timing: only executed when the component is rendered for the first time
// componentDidMount
useEffect(() => {}, [])

// 3 (most frequently used)
// Trigger timing: 1 will be executed for the first rendering 2 will be executed when count (a certain state) changes
// componentDidMount + componentDidUpdate (determine whether count has changed)
useEffect(() => {}, [count])

// 4
useEffect(() => {
  //Execution timing of the return value function: when the component is unloaded
  // In the returned function, clean up the work
  return () => {
  // Equivalent to componentWillUnmount
  }
}, [])

//5
useEffect(() => {
  
  //Execution timing of the return value function: 1 when the component is unloaded 2 when the count changes
  // In the returned function, clean up the work
  return () => {}
}, [count])
  • Send request

  • In components, you can use useEffect Hook to send requests (side effects) to obtain data.

  • Note: effect can only be a synchronous function and cannot use async

    • Because if the effect is async, the return value is a Promise object. In this case, there is no guarantee that the cleanup function will be called immediately

  • In order to use async/await syntax, you can create an async function inside the effect and call

//Error demonstration: Do not add async to effect
useEffect(async () => {
  const res = await axios.get('http://xxx')
  return () => {}
}, [])

// use correctly
useEffect(() => {
  const loadData = async () => {
    const res = await axios.get('http://xxx')
  }
  loadData()
  
  return () => {}
}, [])

useContext-Hooks

Function

  • In the function component, get the value in the Context. To be used together with Context.

Grammar

  • Parameters of useContext: Context object, that is: object created through createContext function

  • The return value of useContext: value data provided in Context.Provider

import { useContext } from 'react'

const { color } = useContext(ColorContext)

The difference between useContext Hook and : the location of obtaining data is different

  • : Get Context shared data in JSX

  • useContext: Get Context data in JS code or JSX

const ColorContext = createContext()

const Child = () => {
  // In normal JS code:
  const { color } = useContext(ColorContext)

  return (
    <div>
      The color value obtained by useContext: { color }
      {/* In JSX: */}
    <ColorContext.Consumer>
        {color => <span>The shared data is: {color}</span>}
      </ColorContext.Consumer>
    </div>
  )
}

useState-useRef

useRef-usage

  • Parameters: When obtaining the DOM, they are generally set to null (when obtaining the DOM object, if the DOM object cannot be obtained, the obtained value will be null)

  • Return value: An object containing the current property.

  • Note: As long as you perform DOM operations in React, you can obtain the DOM through useRef Hook (for example, obtain the width and height of the DOM, etc.)

  • Note: useRef can be used not only to operate DOM, but also to operate components.

import { useRef } from 'react'

const App = () => {
  // 1 Use useRef to create a ref object
  const inputRef = useRef(null)

  const add = () => {
    // 3 Access the corresponding DOM through inputRef.current
    console.log(inputRef.current.value)
    inputRef.current.focus()
  }
  
  return (
    <section className="todoapp">
      {/* 2 Set the ref object to the ref attribute value of input. Purpose: associate the ref object to the DOM object corresponding to the input */}
      <input type="text" placeholder="Please enter the content" ref={inputRef} />
      <button onClick={add}>Add</button>
    </section>
  )
}

export default App

Basic introduction to Redux————————–

Function: Centralized storage and management of application status, ignoring the hierarchical relationship between components when dealing with component communication issues, simplifying communication issues between components in large and complex applications, clear data flow, and easy to locate Bug

Redux core concepts

In order to make the responsibilities of each part of the code clear and unambiguous, the Redux code is divided into three core concepts: action/reducer/store

action

  • Describe what to do, characteristics: only describe what to do

  • It is a js object, must have a type attribute, which is used to distinguish the type of action

  • Depending on the function, additional payload payload parameter data can be carried to complete the corresponding function.

  • For example, counter function description
    { type: 'increment', payload: 10 } // + 10
    { type: 'decrement', payload: 10 } // -10
  • In order to make the action function diversified and flexible, it is necessary to use the action creator function to create the action. The purpose is to simplify the repeated creation of the action object when using the action multiple times. The return value of the function is still an action object.
  • const decrement = payload => ({ type: 'decrement', payload })

reducer(function)

  • Used to process actions and update status. It is where Redux status is updated.
  • The function signature is: (prevState, action) => newState

  • Receive the last status and action as parameters, perform different operations according to the type of action, and finally return the new status. Note: This function must have a return value, even if the status has not changed, it must return one time status

  • Convention: reducer is a pure function (the same input always gets the same output) and cannot contain side effects (for example, function parameters cannot be modified, data external to the function cannot be modified, and asynchronous operations cannot be performed wait)

  • For reducer, in order to ensure that reducer is a pure function, do not:

    1. Do not directly modify the value of the parameter state (that is: do not modify the current state directly, but create a new state value based on the current state value)

    2. Do not use impure operations such as Math.random() / new Date() / Date.now() / ajax requests

    3. Don’t let reducers perform side effects

  • // Example:
    // state last state
    // action The current action to be executed
    const reducer = (state=10, action) => {
      switch (action.type) {
        //increment counter
        case 'increment':
          //Return to new state
          // return state + 1
          //Determine how much to increase based on the payload provided in the action
          return state + action.payload
          // Note: There must be default. If the reducer cannot handle an action in the future,
             Just return to the last state directly
        default:
          return state
      }
    }

store (warehouse)

  • Integrate action and reducer, an application has only one store

  • Maintain the status of the application and obtain the status: store.getState()

  • When initiating a status update, action needs to be distributed: store.dispatch(action)

  • Receive reducer as parameter when creating store: const store = createStore(reducer)

  • Subscription (monitoring) status changes: const unSubscribe = store.subscribe(() => {})

  • Unsubscribe status change: unSubscribe()

Core Code

import {createStore} from 'redux'

// create store
// Parameters are: reducer function
const store = createStore(reducer)

// update status
// dispatch dispatch, dispatch. Represents: Distribute an action, that is, initiate a status update
store.dispatch(action)
store.dispatch(increment(2))

// Get status
const state = store.getState()

//Other API------
// Monitor status changes
const unSubscribe = store.subscribe(() => {
  //When the status changes, perform corresponding operations
  // For example, record redux status
  console.log(store.getState())
})

//Cancel monitoring status changes
unSubscribe()

Redux gets the status default value execution process

  • As long as you create the store and pass the reducer to createStore, then Redux will call the reducer once
  • Redux internally calls the reducer for the first time: The default value type and payload of the reducer are (undefined, {type: "@@redux/INITv.a.4.t.t.p"})
  • Because the passed-in status value is undefined and is a random action type, the switch in the reducer must not be able to process the action, and it will definitely go to default. That is, the default value of the status is directly returned: 10
  • After Redux internally obtains the state value store.getState() (for example, 10 here), it uses this state value as the default value of the state in the store.

Redux code execution process

  1. When creating a store, Redux will first call the reducer to obtain the default state (10)

  2. Then dispatch the action store.dispatch(action) to update the status

  3. As long as the dispatch operation is called, the reducer will be called internally in the Redux store to pass in: the last state (in the current example: 10) and the currently passed in action ({ type: ' increment' }), calculate the new state and return

  4. After the reducer is executed, the latest state is handed over to the store, and the store replaces the old state with the latest state. The state update is completed.

import { createStore } from 'redux'
const store = createStore(reducer)

// reducer(10, { type: 'increment' })
function reducer(state = 10, action) {
  console.log('reducer:', state, action)
  switch (action.type) {
    case 'increment':
      return state + 1
    default:
      return state
  }
}

console.log('The status value is:', store.getState()) // 10

//Initiate update status:
// Parameters: action object
store.dispatch({ type: 'increment' })
// Equivalent to: reducer(10, { type: 'increment' })

console.log('After update:', store.getState()) // 11

Introduction to React-Redux

The react-redux library is the React binding library officially provided by Redux. It connects React to Redux and uses Redux for state management in React. React and Redux are two independent libraries with independent responsibilities. Therefore, in order to use Redux for state management in React, a mechanism is needed to associate these two independent libraries together. At this time, the React-Redux binding library is used.

react-redux documentation

The use of react-redux is divided into two major steps:

1 Global configuration (only needs to be configured once)

  1. Install react-redux: yarn add react-redux

  2. Import Provider component from react-redux

  3. Import the created redux warehouse

  4. Use Provider to wrap the entire application

  5. Set the imported store to the Provider’s store property value

index.js

//Import Provider component
import { Provider } from 'react-redux'
//Import the created store
import store from './store'

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

2 Component access (get status or modify status)

Get status: useSelector hook

  • useSelector: Get the status data provided by Redux

  • Parameters: selector function, used to filter out the required status data from the Redux status and return it

  • Return value: filtered status

import { useSelector } from 'react-redux'

const App = () => {
  const count = useSelector(state => state)
  
  return (
  <div>
    <h1>Counter: {count}</h1>
      <button>Value increases</button>
<button>Decrease the value</button>
    </div>
  )
}

Modify status: useDispatch hook

  • useDispatch: Get the dispatch function, distribute actions, and modify the status data in redux

import { useDispatch } from 'react-redux'

const App = () => {
  const dispatch = useDispatch()
  
  return (
  <div>
    <h1>Counter: {count}</h1>
      {/* Call dispatch to distribute action */}
      <button onClick={() => dispatch(increment(2))}>Value increase</button>
<button onClick={() => dispatch(decrement(5))}>Value decreases</button>
    </div>
  )
}

Summary:

  • Any component can be directly connected to Redux, that is, it can directly: 1 modify the Redux status 2 receive the Redux status

  • Moreover, as long as the status in Redux changes, all components that receive the status will be notified, that is, the latest Redux status can be obtained.

  • In this case, two components can communicate directly no matter how far apart they are.

Redux data flow process:

Redux application

Code structure

/store — Created in the src directory to store Redux-related code
/actions — stores all actions
/reducers — stores all reducers
index.js — the entry file of redux, used to create the store

Redux application

ActionType

  • Action Type refers to: the value of the type attribute in the action object

  • Action types are used many times in Redux projects, such as action objects, reducer functions, dispatch(action), etc.

  • Goal: Centralize action types and maintain consistency of action types in the project

  • The value of action type takes the form: 'domain/action' (function/action) for classification processing, for example,

    • Counter: 'counter/increment' represents the increment action in the Counter function

    • Login: 'login/getCode' indicates the action of logging in to obtain the verification code.

    • Personal information: 'profile/get' means getting personal information

Steps:

  1. Create the actionTypes directory or the constants directory in the store directory for centralized processing

  2. Create constants to store action types and export them

  3. Replace the places where action type is used in the project with these constants to maintain the consistency of action type in the project

// actionTypes or constants directory:

const increment = 'counter/increment'
const decrement = 'counter/decrement'

export { increment, decrement }

// --

// use:

// actions/index.js
import * as types from '../acitonTypes'
const increment = payload => ({ type: types.increment, payload })
const decrement = payload => ({ type: types.decrement, payload })

// reducers/index.js
import * as types from '../acitonTypes'
const reducer = (state, action) => {
  switch (action.type) {
    case types.increment:
      return state + 1
    case types.decrement:
      return state-action.payload
    default:
      return state
  }
}
  • Note: Adding additional Action Type will complicate the project structure, so this operation can be omitted (recommended for large projects). However, the domain/action naming method is highly recommended!

Redux application

Separation and merging of Reducers

  • As project functions become more and more complex, it is recommended to use multiple reducers: divided according to project functions, each function uses a reducer to handle the status update of the function
  • There will be multiple reducers in the project, but store can only receive one reducer. Therefore, multiple reducers need to be merged into one reducer before they can be passed to the store.
  • Combination method: Use the combineReducers function in Redux

  • Note: After merging, the state of Redux will become an object, and the structure of the object is the same as the parameter structure of the combineReducers function

    • For example, the Redux status at this time is: { a: aReducer processing status, b: bReducer processing status }

  • The state of the entire Redux application becomes an object. However, for each reducer, each reducer is only responsible for a certain value in the entire state. Each reducer is only responsible for what it wants to process. Status

  • After merging reducers, redux processing method: as long as reducers are merged, all reducers will be executed once no matter what actions are distributed. When each reducer is executing, it will process the action if it can handle it. If it cannot handle it, it will directly return to the previous state. Therefore, an action we distribute can only be processed by a certain reducer, that is, only the state to be processed by this reducer will be modified in the end. The final performance is: the action is distributed, and only the state corresponding to this action in redux is modified. !

import combine {Reducers} from 'redux'

// Counter case, the default value of status is: 0
const aReducer = (state = 0, action) => {}
// Todos case, the default value of status is: []
const bReducer = (state = [], action) => {}

// Merge multiple reducers into one root reducer
const rootReducer = combineReducers({
  aReducer: aReducer,
  bReducer: bReducer
})

// When creating the store, pass in the root reducer
const store = createStore(rootReducer)

// At this point, the merged redux state: { a: 0, b: [] }



After the reducer state is merged, when each state is accessed again, this state is the merged object. You need to access the state on the object.
import { useSelector } from 'react-redux'

const App = () => {
  const count = useSelector(state => state.aReducer)
  const list = useSelector(state => state.bReducer)
}

Note: (Personal understanding of redux)

  • const increment= payload => ({ type: ‘increment’, payload })
  • As a state management tool, redux divides the state of data into three parts: actions, reducers and stores.
  • Action is an object that describes the purpose of data, { type: ‘increment’, payload: 10 }. This purpose object can be understood as a flag. Increment is the flag for increasing the counter, indicating the flag that needs to be added in the future.
  • When we click to add data, the store will be called and the added identifier store.dispacth(increment()) will be called.
  • After store.dispatch initiates a status update, as long as the dispatch operation is called, the reducer will be called internally in the Redux store and the last status and the currently passed in action identifier will be passed in, which is equivalent to giving reduce distributes a status flag "increment", thereby using the switch function to pair the identifier, distribute the correct logical content, calculates the new status and returns
  • After the reducer is executed, the latest state is handed over to the store, and the store replaces the old state with the latest state. The state is updated.

Note:

The use of createStore in redux here is obsolete. If you want to know the experimental process of data distribution and transfer, you can refer to the above. The new usage logic is mentioned in the new article…

syntaxbug.com © 2021 All Rights Reserved.