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:

  • useEffectimplementationcomponentDidMount
useEffect(() = > {
  // todo
  console.log('Implemented componentDidMount')
}, [])

componentDidMount () {
  // todo
}
Copy the code
  • useEffectimplementationcomponentDidUpdate
useEffect(() = > {
  // todo
  console.log('Implemented componentDidUpdate')
})

componentDidUpdate () {
  // todo
}
Copy the code
  • useEffectimplementationcomponentWillUnmount
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