Cause of Hook
In real projects, there is a lot of life cycle code, and the React life cycle RIPS our business logic into separate pieces. With the advent of Hook, we have moved from lifecycle oriented programming to business logic oriented programming. Let’s stop worrying about the React lifecycle and focus on the business logic we need to focus on.
// Class Component
import React from 'react'
export default class App extends React.Component {
state = {
msg: ' '
}
componentWillMount () {
this.setState({ msg: 'hello, world' })
}
render () {
const { msg } = this.state
return (
<div>{msg}</div>)}}Copy the code
// Function Component
import React, { useState } from 'react'
function App () {
const [ msg, setMsg ] = useState(' ')
return (
<div>{msg}</div>)}export defaut App
Copy the code
React provides common hooks
UseState (Status Management)
Call it in the function component to add some internal state to the component. By passing in an initial value, it returns a pair of values: the current state and a function that lets you update it, which you can call anywhere you need to update state. Unlike class Component, this initial value is not necessarily an object.
import React, { useState } from 'react'
function Example() {
// Declare a state variable called "count".
const [ count, setCount ] = useState(0)
// The method is passed as an input parameter
const [ count2, setCount2 ] = useState(() = > 0)
// The input parameter is passed a string
const [ fruit, setFruit ] = useState('banana')
// The input parameter passes an object
const [ user, setUser ] = useState({ name: 'Joe'.age: 12 })
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()= > setCount(count + 1)}>
Click me
</button>
</div>
);
}
Copy the code
Is equivalent to
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0.count2: 0.fruit: 'banana'.user: { name: 'Joe'.age: 12}}}render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={()= > this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>)}}Copy the code
UseEffect (Side effect Management)
Added the ability to manipulate side effects to function components. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in the Class component, but has been consolidated into an API. During the development process, please put all the side effects of the code into useEffect unified management. UseEffect is executed after the browser rendering is complete.
import React, { useState, useEffect } from 'react'
function Example() {
const [count, setCount] = useState(0)
useEffect(() = > {
// Update the page title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()= > setCount(count + 1)}>
Click me
</button>
</div>
);
}
Copy the code
UseEffect has nothing to do with componentDidMount, componentDidUpdate, and componentWillUnmount. Please do not use the same seat:
useEffect
implementationcomponentDidMount
useEffect(() = > {
// todo
console.log('Implemented componentDidMount')
}, [])
componentDidMount () {
// todo
}
Copy the code
useEffect
implementationcomponentDidUpdate
useEffect(() = > {
// todo
console.log('Implemented componentDidUpdate')
})
componentDidUpdate () {
// todo
}
Copy the code
useEffect
implementationcomponentWillUnmount
useEffect(() = > {
return () = > {
// todo
console.log('Implements componentWillUnmount')
}
}, [])
componentDidUpdate () {
// todo
}
Copy the code
So, if you use a timer on a page and want to turn it off when a component is removed, you can do so
useEffect(() = > {
const timer = setInterval(() = > {
console.log('I am a little stream, always flowing on, little little stream, never to stay.')},1000)
return () = > {
clearInterval(timer)
}
}, [])
Copy the code
If you have many states in your component, but you only rely on one or more of them in the side effect logic, you can do this:
import React, { useState, useEffect } from 'react'
function Example() {
const [ count, setCount ] = useState(0)
const [ fruit, setFruit ] = useState('apple')
const [ user, setUser ] = useState({ name: 'Joe'.age: 10 })
useEffect(() = > {
console.log(`${user.name}Love to eat${fruit}`)
}, [ user, fruit ]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={()= > setCount(count + 1)}>
Click me
</button>
</div>
);
}
Copy the code
useCallback
Passing the inline callback function and the array of dependencies as arguments to useCallback returns the Memoized version of the callback function, which is updated only when a dependency changes.
import React, { useState, useCallback } from 'react'
function Example() {
const [count, setCount] = useState(0);
const [msg, setMsg] = useState('yes');
/ / method
const sayHello = useCallback(() = > {
setCount(count + 1)
}, [ count ]);
return (
<div>
<p>{msg}</p>
<button onClick={()= > setMsg(msg === 'yes' ? 'no' : 'yes')}>say yes</button>
<p>{count}</p>
<Info say={sayHello} />
</div>
);
}
const Info = (props) = > {
return (
<button onClick={props.say}>say hello</button>)}Copy the code
“SayHello” works if you write “sayHello” as follows.
/ / method 2
const sayHello = () = > {
setCount(count + 1)}Copy the code
From the running effect, indeed can run, and no error. But you can add the props. Say method to the Info component and see:
const Info = (props) = > {
useEffect(() = > {
console.log('this is effect')
}, [ props.say ])
return (
<button onClick={props.say}>say hello</button>)}Copy the code
When you click say Yes, you’ll notice that method two keeps printing this is effect on the console, but method two doesn’t print it. Therefore, it is appropriate to wrap useCallback around methods that will be passed to child components during our development. Is there any room to optimize the above example? The answer: Yes!
import React, { useState, useCallback } from 'react'
function Example() {
const [count, setCount] = useState(0);
const [msg, setMsg] = useState('yes');
const sayHello = useCallback(() = > {
setCount(count + 1)
}, [ count ]);
return (
<div>
<p>{msg}</p>
<button onClick={()= > setMsg(msg === 'yes' ? 'no' : 'yes')}>say yes</button>
<p>{count}</p>
<Info say={sayHello} />
</div>
);
}
const Info = React.memo((props) = > {
useEffect(() = > {
console.log('this is effect')})return (
<button onClick={props.say}>say hello</button>)})Copy the code
At this point you will notice that clicking say yes, Info has no side effects at all. Perfect! To recap: What if I want to click on the say Hello method inside the Info component, useCallback still caches the callback, but it needs count to get the latest value? The answer: useRef + useLayoutEffect!
import React, { useState, useRef, useLayoutEffect, useEffect, useCallback } from 'react'
function Example() {
const [count, setCount] = useState(0);
const [msg, setMsg] = useState('yes');
const ref = useRef()
// When the value changes, the latest value is assigned to ref
useLayoutEffect(() = > {
ref.current = count
}, [ count ])
// if ref does not change, the previous cached callback will always be returned
const sayHello = useCallback(() = > {
setCount(ref.current + 1)
}, [ ref ]);
return (
<div>
<p>{msg}</p>
<button onClick={()= > setMsg(msg === 'yes' ? 'no' : 'yes')}>say yes</button>
<p>{count}</p>
<Info say={sayHello} />
</div>
);
}
const Info = React.memo((props) = > {
useEffect(() = > {
console.log('this is effect')})return (
<button onClick={props.say}>say hello</button>)})Copy the code
If you don’t understand the use of useRef and useLayoutEffect, you can put it down. When you click on the say Hello button of the Info component, you will find that there are no side effects in Info. Perfect!
useRef
UseRef returns a mutable ref object whose.current property is initialized as the passed parameter (initialValue). The ref object returned remains constant throughout the life of the component.
const refContainer = useRef(initialValue)
refContainer.current = newValue
Copy the code
import React, { useRef } from 'react'
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () = > {
// 'current' points to the text input element mounted to the DOM
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
export default TextInputWithFocusButton
Copy the code
useMemo
You pass in the create function and the dependency array as arguments to useMemo, which recalculates memoized values only when a dependency changes. This optimization helps avoid costly calculations every time you render. This is consistent with computed in Vue.
const memoizedValue = useMemo(() = > computeExpensiveValue(a, b), [a, b]);
Copy the code
UseCallback (fn, deps) is equivalent to useMemo(() => FN, deps).
useLayoutEffect
The use is the same as useEffect, but useLayoutEffect is executed before the browser rendering is complete.
function Example() {
useLayoutEffect(() = > {
console.log("useLayoutEffect ...");
});
useEffect(() = > {
console.log("useEffect ...");
});
return <div></div>;
}
Copy the code
In the example above, “useLayoutEffect…” is printed first. And print “useLayoutEffect…
useContext
Receives a context object (the return value of React. CreateContext) and returns the current value of the context. The current context value is determined by the < MyContext.provider > value prop of the upper-layer component closest to the current component.
import React, { useContext } from 'react'
const theme = {
light: {
color: 'white'.bg: 'black'
},
dark: {
color: 'black'.bg: 'white'}}// Create the context and set the default value
const ThemeContext = React.createContext(theme.light)
function Example () {
return (
<ThemeContext.Provider value={theme.light}>
<div><Info /></div>
</ThemeContext.Provider>)}function Info () {
const ctx = useContext(ThemeContext)
return (
<div style={{ color: ctx.color.backgroundColor: ctx.bg}} >The main content</div>)}export default Example;
Copy the code