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
- Page rendering, console.log(‘ first rendering and data changes are performed ‘), return function not executed
- 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
- No second argument, this is the easiest, no return function, it’s equivalent to componentDidMount + componentDidUpdate
- ComponentDidMount + componentDidUpdate + componentWillUnmount But because changing the data interferes with executing the return function, there are no true three life cycles
- 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
- ComponentDidMount () {componentWillUnmount ();} return () {componentDidMount ();
- When passing an array containing variables, there is no return function. UseEffect is executed only when the variables in the array change
- 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
- Create createContext
const NumContext = createContext()
Copy the code
- 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
- 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