React Hooks

Which is in keeping with the hooks that are currently in use. To sum up some of my thoughts and ideas, I recently learned from hooks that are in use. I feel that hooks are a trend, that after I use them I cannot get back

Introduce the React Hooks

Hooks now declare components functionally instead of using classes. Stateful and stateless components are now stateless. It also makes unit testing easier. Because there is no class declaration, there is no life cycle. But the declaration cycle is an important concept we’ve always had in React. React or Vue. Declaration cycles are always an important piece of knowledge. But hooks. It’s a different way of thinking that helps us implement our business with less code and more elegant ideas. In other words,hooks implement the lifecycle as well, maybe it does it better.

What are the Hooks?

First, among other things, it writes clean code

A class of writing:

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      num: 0
    }
  }
  addNum = () => {
    this.setState((prevState) => ({
      num: prevState.num + 1
    }))
  }
  render() { 
    return( <div> <p>{this.state.num}</p> <button onClick={this.addNum}>Chlick me</button> </div> ); }}export default App;
Copy the code

Which is the result of hooks:

import React, { useState } from 'react';

const App = () => {
  const [num, setNum] = useState(0)

  return (
    <div>
      <p>{num}</p>
      <button onClick={() => {setNum(num + 1)}}>Add</button>
    </div>
  )
}
 
export default App;
Copy the code

Say goodbye to state, say goodbye to life cycle, say goodbye to class, and most importantly, we don’t have to bind this! With this basic 👋

Which of the following are hooks that are used in the system: use useState. Which of the following are hooks that are used in the system

useState

UseState is used to declare state variables

A declarative way

const [num, setNum] = useState(0)
Copy the code
Const [variable name, function name] = useState(initial value)Copy the code

Using ES6 destruct assignment, the variable and function names are arbitrary, but for the sake of our own identification, the function names are used to write setFunc.

Of course we can declare more than one variable

const [num1, setNum1] = useState(0)
const [num2, setNum2] = useState({age: 25})
const [num3, setNum3] = useState ([1, 2, 3])...Copy the code

UseState not only accepts primitive types and objects, arrays, but can also pass in a function, which is executed only once.

import React, { useState } from 'react';

const App = ({price}) => {
  const [num, setNum] = useState(() => price || 5)

  return (
    <div>
      <p>{num}</p>
      <button onClick={() => {setNum(num + 1)}}>Add</button>
    </div>
  )
}
 
export default App;
Copy the code

This is easy, just put the variable name in JSX and it’s OK, but not this.state as before.

<p>{num}</p>
Copy the code

Modify variables

It used to be setState, but now we’re going to use setNum, which we just declared

<button onClick={() => {setNum(num + 1)}}>Add</button>
Copy the code

In addition, useState remembers the value of the previous render, so if we want to fetch the value before this render, we can pass in an anonymous function to fetch it

<button onClick={() => {setNum((num) => num + 1)}}>Add</button>
Copy the code

Do not use hooks in conditions such as conditional statements, because useState is declared in an array, and when you change the order of useState, the data for that useState will get messed up. Result in an error

import React, { useState } from 'react';

const App = ({price}) => {
  let num, setNum
  if (Math.random > 0.5) {
    [num, setNum] = useState(1)
  } else {
    [num, setNum] = useState(2)
  }

  return (
    <div>
      <p>{num}</p>
      <button onClick={() => {setNum(num + 1)}}>Add</button>
    </div>
  )
}
 
export default App;
Copy the code

useEffect

UseEffect performs a number of side effects when data changes

UseEffect can be used as componentDidMount, componentDidUpdate, componentWillUnmount life cycle, but if you want to use useEffect or some effort. However, if you use it well, you will find that useEffect is really useful.

Since useEffect can be used to implement the lifecycle, how exactly do you implement the lifecycle

use

import React, { useState, useEffect } from 'react';

const App = ({price}) => {
  const [num, setNum] = useState(() => price || 5)

  useEffect(() => {
    console.log('num change executes useEffect')})return (
    <div>
      <p>{num}</p>
      <button onClick={() => {setNum(num + 1)}}>Add</button>
    </div>
  )
}
 
export default App;
Copy the code

UseEffect is executed once for the first rendering and data changes, so useEffect is equal to componentDidMount + componentDidUpdate ** UseEffect is asynchronous, so it does not block the rendering view of a page, but componentDidMount + componentDidUpdate is synchronous. Use the useLayoutEffect if you want to measure the width and height of the layout

So let’s implement componentWillUnmount

To implement componentWillUnmount, you must return a function to unbind. Look at the code

import React, { useState, useEffect } from 'react';

const App = () => {
  const [num, setNum] = useState(0)
  const [width, setWidth] = useState(document.body.clientWidth)

  const onChangeSize = () => {
    setWidth(document.body.clientWidth)
  }
	
  useEffect(() => {
    console.log('First render and changes to data are performed')
    window.addEventListener('resize', onChangeSize)
    return () => {
      console.log('Unload components and change data execution')
      window.removeEventListener('resize', onChangeSize)
    }
  })

  return (
    <div>
      <p>{width}</p>
      <button onClick={() => {setNum(num + 1)}}>Add</button>
    </div>
  )
}
 
export default App;
Copy the code

As we can see from the code, we can unbind by returning a function. However, if you modify num data, return will also perform the useEffect execution order

  1. Page rendering, console.log(‘ first rendering and data changes are performed ‘), return function not executed
  2. After changing data, return functions console.log(‘ unload component and change data execution ‘) and console.log(‘ first render and change data execution ‘)

We can see that our requirement implementation is problematic, I want to implement listening in **componentDidMount **, componentWillUnmount implementation unbind, but because change data also perform unbind operation, this is problematic, so we need the second function useEffect

So let’s implement the above requirements

import React, { useState, useEffect } from 'react';

const App = () => {
  const [num, setNum] = useState(0)
  const [width, setWidth] = useState(document.body.clientWidth)

  const onChangeSize = () => {
    setWidth (document. Body. ClientWidth)} / / implementation requirements useEffect useEffect (() = > {/ / componentDidMount window. The addEventListener ('resize', onChangeSize)
    return () => {
      // componentWillUnmount
      window.removeEventListener('resize', onChangeSize)
    }
  }, [])

  useEffect(() => {
    // componentDidMount + componentDidUpdate
    document.title = num
  })

  useEffect(() => {
    // componentDidMount
    setTimeout(() => {
      console.log('the ajax request'})}, 1000), []) useEffect (() = > {the console. The log (` num change for${num}`)
  }, [num])

  return (
    <div>
      <p>{width}</p>
      <button onClick={() => {setNum(num + 1)}}>Add</button>
    </div>
  )
}
 
export default App;

Copy the code

Passing an empty array means that the useEffect is no longer relevant to the data.

In fact, this array is used to tell useEffect who is affected by it. Since you wrote an empty array, it has nothing to do with any state data. If you want to associate the number of states, line 33 of the code above clearly states that only num can affect its useEffect. You also write multiple states to disassociate this useEffect.

UseEffect can be written multiple times. It’s not going to have to be written all together and make a lot of judgments like the previous statement cycle

So let’s make a summary

The second parameter to useEffect has six cases

  1. No second argument, this is the easiest, no return function, it’s equivalent to componentDidMount + componentDidUpdate
  2. ComponentDidMount + componentDidUpdate + componentWillUnmount But because changing the data interferes with executing the return function, there are no true three life cycles
  3. If you pass in an empty array [] without a return function, it will only be called once. This means that useEffect has nothing to do with data. But it does that the first time you render it, equivalent to componentDidMount
  4. ComponentDidMount () {componentWillUnmount ();} return () {componentDidMount ();
  5. When passing an array containing variables, there is no return function. UseEffect is executed only when the variables in the array change
  6. ComponentDidMount = componentDidUpdate = componentDidMount = componentDidUpdate Not rigorous componentWillUnmount, because it again because of the data to execute the return function

Therefore, if you want to achieve the effect of the life cycle, use 1,3,4. The second one is not recommended, the logic is confusing, the fifth one can be used when you do some logical requirements, and the sixth one I haven’t tried very much, because I try to write them in pieces.

In fact, I think there is no need to write so complex, can split or split it

useContext

Helps us get the variables passed across the hierarchy of components

Here’s a 🌰:

First of all, we have a situation where the grandfather component App, the son component Detail, and the grandson component Btn. The grandfather has a variable to pass to the grandson, but it is very troublesome to pass it to the son. Generally, we need props to pass it to the son. But obviously if the grandfather is older and has a great grandchild, then we’re in more trouble. PS: Context and redux are not dealing with the same thing, one is dealing with passing values, one is dealing with global data management

App.js import React, {useState, createContext} from'react';
import Detail from './Detail'
const NumContext = createContext()

const App = () => {
  const [num, setNum] = useState(0)

  return (
    <div>
      <p>{num}</p>
      <button onClick={() => {setNum(num + 1)}}>Add</button>
      <NumContext.Provider value={num}>
        <Detail />
      </NumContext.Provider>
    </div>
  )
}
 
export {App, NumContext};
Copy the code
Detail Btn import React, {useContext} from'react'
import { NumContext } from './App'

const Detail = () => {
  return <div id="Detail"><Btn /></div>
}

const Btn = () => {
  const num = useContext(NumContext)
  return <button>{num}</button>
}

export default Detail
Copy the code

You want to implement context in total points

  1. Create createContext
const NumContext = createContext()
Copy the code
  1. Using the component that we’ve created we’ll write a closing tag where value is the variable that you want to provide
< numContext. Provider value={num}> // Component.. </NumContext.Provider>Copy the code
  1. In the grandchild component, import the context component that you created on the parent, and use our hooks to get the value
import React, {useContext} from 'react'
import { NumContext } from './App'

const Btn = () => {
  const num = useContext(NumContext)
  return <button>{num}</button>
}
Copy the code

From this we can see that no matter how many levels, it is accessible

useReducer

The useReducer actually simulates the Reducer of Redux, and it is often used in conjunction with useContext to achieve the same effect as Redux

See 🌰 👇 :

import React, { useReducer } from 'react';

const App = () => {
  const [state, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'increase':
        return {num: state.num + 1}
      case 'decrease':
        return {num: state.num - 1}
      default:
        return state
    }
  }, {
    num: 0
  })

  return (
    <div>
      <p>{state.num}</p>
      <button onClick={() => {dispatch({type: 'increase'})}}>increase</button>
      <button onClick={() => {dispatch({type: 'decrease'})}}>decrease</button>
    </div>
  )
}
 
export default App;
Copy the code

The first parameter is a reducer function. The second parameter is the default state value, which can return two values, state and Dispatch

UseContext UseReducer implements Redux

Let me briefly share my understanding of using the hooks feature to implement similar functionality in Redux

The directory is as follows:

In fact, the same way as before, a little bit different

The first is index – this file is the root file, and we make store data available to all components throughout the project:

// index.js 
import React from 'react'
import {Color} from './store/state'
import Home from './components/Home'

const App = () => {
  return(< div > < Color > {/ * contains all subcomponents, here in the Home, for example * /} < Home / > < / Color > < / div >)}export default App
Copy the code

👇 Look at the contents of the store, in fact, the store file has been imported above

// state.js

import React, { createContext, useReducer } from 'react'
import {reducer} from './reducer'// 1. DefaultState const defaultState = {color:'blue'} // 2. Create a context that allows all wrapped components to get state, dispatchexport const DataContext = createContext()

exportConst Color = props => {// 3. UseReducer props => {// 3. dispatch] = useReducer(reducer, defaultState)return (
    <DataContext.Provider value={{state, dispatch}}>
      {props.children}
    </DataContext.Provider>
  )
}
Copy the code

At this point, we are more than halfway there. Since we have put state and dispatch into value, all components can get state globally

Now this is easy.

// reducer.js

import { UPDATE_COLOR } from './constants'

export const reducer = (state, action) => {
  switch (action.type) {
    case UPDATE_COLOR:
      return {color: action.color}
    default:
      return state
  }
}
Copy the code
// constants.js

export const UPDATE_COLOR = 'UPDATE_COLOR'
Copy the code
// actionCreators.js

import { UPDATE_COLOR } from './constants'

export const updateColor = (color) => ({
  type: UPDATE_COLOR,
  color
})
Copy the code

So we’re done here

The demo code

useMemo

UseMemo is useful because it helps us save resources

An 🌰 first:

import React, { useState, useMemo } from 'react';

const App = () => {
  const [a, setA] = useState('a')
  const [b, setB] = useState('b')

  return (
    <div>
      <p>{a} App</p>
      <p>{b} App</p>
      <button onClick={() => {setA(a + a)}}>aaa</button>
      <button onClick={() => {setB(b + b)}}>bbb</button>
      <Children theA={a}>{b}</Children>
    </div>
  )
}

const Children = ({theA, children}) => {

  console.log('Children re-render')

  const aChange = (getA) => {
    console.log('useMemo')
    return getA + ' useMemo'
  }

  return (
    <div>
      <p>{aChange()}</p>
      <p>{children}</p>
    </div>
  )
}

export default App;
Copy the code

We found that if you do not use useMemo, aChange will execute when you change the value of B, that is, if you change any variable of the parent component, it will affect Children. In fact, the a variable does not change at all, but aChange still executes

So we need to use useMemo to control it

import React, { useState, useMemo } from 'react';

const App = () => {
  const [a, setA] = useState('a')
  const [b, setB] = useState('b')

  return (
    <div>
      <p>{a} App</p>
      <p>{b} App</p>
      <button onClick={() => {setA(a + a)}}>aaa</button>
      <button onClick={() => {setB(b + b)}}>bbb</button>
      <Children theA={a}>{b}</Children>
    </div>
  )
}

const Children = ({theA, children}) => {

  console.log('Children re-render')

  const aChange = (getA) => {
    console.log('useMemo')
    return getA + ' useMemo'
  }

  const aVal = useMemo(() => aChange(theA), [theA])

  return (
    <div>
      <p>{aVal}</p>
      <p>{children}</p>
    </div>
  )
}

export default App;

Copy the code

With useMemo, the first function that takes the value you are evaluating and the second parameter that takes the value of the variable in the array will execute the useMemo function

useRef

UseRef can help us to obtain an object with the same lifetime

import React, { useState, useRef } from 'react';

const App = () => {
  let [num, setNum] = useState(0);
  return (
      <div>
          <Children />
          <button onClick={() => setNum({ num: num + 1 })}>+</button>
      </div>
  )
}

let input;
function Children() {
    const inputRef = useRef()
    console.log(input === inputRef)
    input = inputRef
    return <input type="text" ref={inputRef} />
}

export default App;

Copy the code

Customize the Hook

A custom hook is a bit like a function we write, but a custom hook has its own state. It just helps us to reuse the logic, but it gets its own state every time it is called. In addition, the change of the result returned by the custom hook will also render the parent component. In terms of naming, we usually use use as the way to name the custom hook. Of course, this is an unwritten rule

import React, { useState, useEffect } from 'react';

const useEnter = (key) => {
  const [hasPressed, setHasPressed] = useState(false)

  const keyDown = ({keyCode}) => {
    if (key === keyCode) {
      setHasPressed(true)
    }
  }

  const keyUp = ({keyCode}) => {
    if (key === keyCode) {
      setHasPressed(false)
    }
  }

  useEffect(() => {
    // console.log('addEventListener')
    document.addEventListener('keydown', keyDown)
    document.addEventListener('keyup', keyUp)
    return () => {
      // console.log('removeEventListener')
      document.removeEventListener('keydown', keyDown)
      document.removeEventListener('keyup', keyUp)
    }
  })
  return hasPressed
}

const App = () => {
  console.log('render')
  const [name, setName] = useState('kun')
  const isEnter = useEnter(13)
  useEffect(() => {
    if (isEnter) {
      setName('flower')}else {
      setName('kun')
    }
  }, [isEnter])
  return (
    <div>
      <p style={{fontSize: '50px'}}>{name}</p>
    </div>
  )
}

export default App;

Copy the code