What are hooks?

The official as saying

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class

Even in the React function component, you can use the state and component lifecycle of the Classes Components.

why hooks ?

Reuse problem

It’s hard to reuse a stateful component, but it can be reused. What did the authorities do? RenderProps and hoc, but you’ll find that your components are layered, nested, and deep, making it difficult to locate when things go wrong.

Recompose is the most familiar library in this area, but the author has already joined the React team and posted a statement

Hi! I created Recompose about three years ago. About a year after that, I joined the React team. Today, we announced a proposal for Hooks. Hooks solves all the problems I attempted to address with Recompose three years ago, and more on top of that. I will be discontinuing active maintenance of this package (excluding perhaps bugfixes or patches for compatibility with future React releases), and recommending that people use Hooks instead. Your existing code with Recompose will still work, just don’t expect any new features. Thank you so, so much to @wuct and @istarkov for their heroic work maintaining Recompose over the last few years.

Hooks fixed all the problems I tried to fix with Recompose three years ago, and more, and stopped maintaining the library.

The life cycle

React has too many different life cycles compared to VUE. Hooks are a life cycle useEffect, which you can think of as a set of componentDidMount, componentDidUpdate, and componentWillUnmount. I will describe them in the following section.

The problem of this

class App extends Component { constructor(props) { super(props); this.state = { count: 0, count1: 0, count2: 0 }, this.setCount = this.setCount.bind(this); } setCount () {} setcout = () => {} render() {<button onClick={this.setcount} OnClick ={this.setCount}> test 1</button>}}Copy the code

You have to be careful with this even if you use bind or arrow. But there is no concept of this in FP programming

The preparatory work

React >=16.8 React native >= 0.59Copy the code

Commonly used API

  • useState
  • useEffect
  • useReducer
  • useRef
  • useContext

Let’s take a look at each of them

const App = () => { const [loading, updateLoading] = useState(true) const [list, updateList] = useState([]) const [title, updateTitle] = useState(undefined) useEffect(() => { getData() }, []) const getData () = = > {the fetch (' https://api.douban.com/v2/movie/in_theaters?city= guangzhou & start = 0 & count = 10 '). Then, the response  => response.json() ) .then(res => { updateTitle(res.title) updateList(res.subjects) updateLoading(false) }) } return ( <View style={styles.container}> <Text style={styles.welcome}>{title}</Text> {loading? <Text> Loading --</Text> : list.map(i => <Text key={i.id} style={styles.instructions}>{i.title}</Text>)} </View> ) }Copy the code

useState

 const [loading, updateLoading] = useState(true)
Copy the code

Define state. The first value in the array is the value you need to update, and the second value is the initial value passed in by the useState() function. Just call updateLoading (true) when you update

useEffect

Popular point is componentDidMount, componentDidUpdate, componentWillUnmount three life cycle collection. It must trigger after rendering. If you update the loading value with updateLoading and the page is rendered again, this method is triggered.

The question is, won’t my code repeat the request until 💥💥💥? UseEffect can be passed in as a second parameter to avoid the performance penalty, bypassing the change if the member variables in the second parameter array have not changed. If you pass an empty array, this effect will only be executed when the component is mounted or unmounted. It is generally best to pass a unique ID.

How do I do some cancellations?

Timers, publish subscriptions, and sometimes when a component is uninstalled and you want to cancel it what do you do? You can return a new function

Watch out for a pothole

I found a problem when I tried to simulate the request with the timer.

function Counter() {
  let [count, setCount] = useState(0);
  useEffect((a)= > {
    let id = setInterval((a)= > {
      setCount(count + 1);
    }, 1000);
    return (a)= >clearInterval(id); } []);return <Tex>{count}</Text>;
}
Copy the code

I realized my number is 1 and I’m not moving!! why ?

The problem is that useEffect gets a count of 0 on the first rendering and we don’t redo effect anymore, so setInterval keeps referring to the closure count 0 on the first rendering so that count + 1 is always 1. Over time, new apis were found to circumvent the problem. Either by using useRef or by using useReducer

useReducer

An alternative to useState. Accept the Reducer of type (state, action) => newState and return the current state matched with the Dispatch method. (If you’re familiar with Redux, you already know how it works.) Those who have used Redux should be familiar with this reducer. 🌰 actions. Js

export const loading = 'LOADING'
export const list = 'LIST'
export const updateLoading = (data) => ({
  type: loading,
  data
})
export const updateList = (data) => ({
  type: list,
  data
})
    
Copy the code

🌰 reducer. Js

import {
  loading,
  list,
} from './actions'
export const initState = {
  loading: true.list: [],}export const initReducer = (state, {type, data}) = > {
  switch (type) {
    case list:
      return {
        ...state,
        list: data
      }
    case loading:
      return {
        ...state,
        loading: data
      }
    default:
      return state
  }
}

Copy the code

Last connected component

import React, { useReducer, useEffect, useRef } from 'react'; import {Platform, StyleSheet, Text, View} from 'react-native'; import { updateLoading, updateList } from './store/actions' import { initState, initReducer } from './store/reducers' const App = () => { const [state, dispatch] = useReducer(initReducer, initState) useEffect(() => { getData() }, []) const getData () = = > {the fetch (' https://api.douban.com/v2/movie/in_theaters?city= guangzhou & start = 0 & count = 10 '). Then, the response  => response.json() ) .then(res => { dispatch(updateList(res.subjects)) dispatch(updateLoading(false)) }) } const {list Loading} = state return (<View style={styles.container}> {loading? <Text> Loading --</Text> : list.map(i => <Text key={i.id} style={styles.instructions}>{i.title}</Text>)} </View> ) } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, }); export default AppCopy the code

rendering

useRef

That’s nothing. What about the current layer

import React, { useReducer, useEffect, useRef } from 'react';
import {Platform, StyleSheet, Text, View, TextInput } from 'react-native';
import { updateLoading, updateList } from './store/actions'
import { initState, initReducer } from './store/reducers'

const App = (a)= > {
  const _ref = useRef()
  const [state, dispatch] = useReducer(initReducer, initState)
  useEffect((a)= > {
    getData()
  }, [])
  const getData = (a)= > {
    fetch('https://api.douban.com/v2/movie/in_theaters?city=, guangzhou & start = 0 & count = 10')
    .then(response= > response.json()
    )
    .then(res= > {
      dispatch(updateList(res.subjects))
      dispatch(updateLoading(false))})}const {list = [], loading} = state 
  return (
    <View style={styles.container}>
      {loading ? <Text>- in the load</Text> : list.map(i => <Text key={i.id} style={styles.instructions}>{i.title}</Text>)}
      <TextInput ref={_ref} />
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
export default App
Copy the code

Custom hooks

What is better for hooks is that you can customize them. For example, if I now want to wrap a request hooks can do this

import React, { useState, useEffect } from 'react'; export const request = (initData) => { const [data, updateData] = useState(initData) const [url, updateUrl] = useState(null) const [isLoading, updateLoading] = useState(true) const [isError, updateError] = useState(false) useEffect(() => { fetchData() }, [url]) const fetchData = () => { if(! url) return updateLoading(true) try { fetch(url).then(res => res.json()).then(res => { updateData(res) }) } catch (error) { updateError(error) console.log(error) } updateLoading(false) } const doFetch = url => { console.log(url, 'url') updateUrl(url) } return { data, isLoading, isError, doFetch } }Copy the code

How do I call it

const { data, isLoading, doFetch } = request([]) useEffect(() => { getData() }, []) const getData = async () = > {const url = "https://api.douban.com/v2/movie/in_theaters?city= guangzhou & start = 0 & count = 10 ' doFetch(url) }Copy the code

conclusion

When we first came out in beta, we were on the fence. Now that I’ve been using it on PC for a while, it smells good. I like hooks more and more.