Use Hooks in React Native

React officially announced that React v16.7.0-alpha (Beta) will introduce Hooks at the 2018 ReactConf conference. What is Hooks, let's find out.

What are Hooks?

In the usual development process, we generally encounter the following problems:

1. It is difficult to reuse and share state-related logic
in components 2. Complex logic components are difficult to develop and maintain. When our components need to handle multiple unrelated states, each life cycle function may contain Various unrelated logics are inside.
3. Due to business changes, function components have to be changed to class components and so on.

The above is more abstract. Next, we will use the Api Keyboard as an example to illustrate the problem.

export default class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      isShowKeyboard: false
    }
  }

  static getDerivedStateFromProps() {
    this.keyboardDidShowListener = Keyboard.addListener(
      "keyboardDidShow",
      this.keyboardDidShowHandler
    );
  }

  keyboardDidShowHandler () {
    this.setState({
      isShowKeyboard: true
    });
  }

  componentWillUnmount() {
    this.keyboardDidShowListener.remove();
  }

  render() {
    return (
      <View style={styles.container}>
        <Text>
          当前键盘状态: {this.state.isShowKeyboard}
        </Text>
      </View>
    );
  }
}

The above code use case is relatively simple, use Keyboard to register to monitor the display and hidden state of the keyboard . You can see the registration, logout, and state render of the keyboard event are placed in the Component. If a lot of such logic is involved in the current Component, it will cause the current Component to have very heavy responsibilities and state data cannot be shared. When you need to monitor keyboard events, you need to rewrite or copy duplicate code. The redundancy is very serious, and it is not a good thing for function maintenance and expansion.

The emergence of Hooks solves the above problem. It allows developers to define function components, and can also use the state and life cycle of class components, without the need to go back and forth between mixins, function components, HOC components, and render props. Switch. It is convenient for us to realize the separation of business logic code and the reuse of components in the business. Compared to using setState, components are stateless. Let's see how to use Hooks:

import { useState, useEffect } from 'react';
import {
    Keyboard
} from 'react-native';

export default function keyboard() {
    const [keyboardStatus, setKeyboardStatus] = useState(false);
    Keyboard.addListener(
        "keyboardDidShow",
        this.keyboardDidShowHandler
    );
    useEffect(()=> {
        return ()=> Keyboard.removeListener(
            "keyboardDidShow",
            ()=> setKeyboardStatus(true)
        );
    }, [false]);
    return (
        <Text>
            当前键盘状态:{keyboardStatus}
        </Text>
    )
}

In the above code, the business logic about the keyboard is stripped into functions, called  function components. When we use it in other components, we only need to import it. In the function component, we use useState and useEffect. What role do they play as APIs provided in Hooks?

Hooks Api

The official provides three key APIs of hooks, namely State Hooks, Effect Hooks, Context Hooks, Custom Hooks (custom hooks).

useState

useState This method can bring local state to the function component, it receives a value for the initial state and returns a pair of variables

// 等价于
const keyboardStatus= useState(0)[0];
const setKeyboardStatus= useState(0)[1];

It is relatively simple to understand, in fact, it is to define the state state value and modify the behavior function of the state state value.

useEffect

useEffect can be simply understood as a replacement for the following life cycle:

componentDidMount、componentDidUpdate、componentWillUnmount 

The code of useEffect will be executed not only during the first initialization (componentDidMount), but also every time a subsequent render is triggered (componentDidUpdate), and the return value is executed when the component is unregistered (componentWillUnmount). Combined with the above example:

useEffect(()=> {
   // return 将会在组件注销时调用
   return ()=> Keyboard.removeListener(
       "keyboardDidShow",
       ()=> setKeyboardStatus(true)
   );
}, [false]);

The second parameter of useEffect, as a performance optimization setting, decides whether to perform the operations inside to avoid some unnecessary performance loss. As long as the value of the member in the second parameter array has not changed, this execution will be skipped. If an empty array [] is passed in, it will only be executed during component mount and unmount.

Context Hooks

A new Context API was released in React 16.3. The purpose is to solve the problem that the nesting level of child components is too deep and the properties of parent components are difficult to communicate. The method of use is not complicated. First, use the Context API to create a data provider (Provider) and a data consumer (Consumer). (Speaking of this is a bit like the example of Java multi-threaded concurrency) Store data in the place where the provider is located, and fetch the data where the consumer is located. Take a brief look at how Context is used:

(1) Create context

// 创建 Context
import React from 'react';
const DataContext = React.createContext({
    name: '',
    age: 23
});
export default DataContext;


(2) Define the data provider Provider

export default class App extends Component {

  render() {
    return (
      <DataContext.Provider value={{ name: 'Songlcy', age: 27 }}>
        <CustomComponent />
      </DataContext.Provider>
    );
  }
}

(3) Define Consumer

export default class CustomComponent extends Component {
    render() {
        return (
            <DataContext.Consumer>
                {
                    context => (
                        <Text>
                            我的名字:{context.name}, 我的年龄:{context.age}
                        </Text>
                    )
                }
            </DataContext.Consumer>
        )
    }
}

When the component nesting level is deep, the advantages of Context will be more obvious.

" Eh, wake up! " ... said so much, continue to return to Hooks. In the above code, the data is obtained from the Context — Provider — Consumer , and the entire value process is relatively tedious. When we want to get values from multiple  Consumers , it is even more troublesome to nest functions.

useContext is a simplification of the Context API. Let's see what it looks like after simplification:

const { name, age } = useContext(DataContext);

" I rely on! Is this the end? " Yes, the value process is so simple and so capricious.  How about 10 more  consumers !

Custom Hooks

Custom Hooks is a custom Hooks behavior, which is not an API in itself. The core concept is to extract the logic and encapsulate it into the function. The specific implementation is to encapsulate the logic related to the state data (State) through a function and extract these logic from the component. In this function we can use other Hooks, or we can test it separately. Modify the above example:

export default function useKeyboardStatus() {
    const [keyboardStatus, setKeyboardStatus] = useState(false);
    Keyboard.addListener(
        "keyboardDidShow",
        this.keyboardDidShowHandler
    );
    useEffect(()=> {
        return ()=> Keyboard.removeListener(
            "keyboardDidShow",
            ()=> setKeyboardStatus(true)
        );
    },[]);
    return keyboardStatus;
}

Code is almost identical, the only difference is the name of the function with the use * prefix here need to follow a convention, named use  use * .

How Hooks work

"Shenma? Hooks is actually an array !"

Recall the way we used useState initially:

const [keyboardStatus, setKeyboardStatus] = useState(false);

In fact, from this code, we can also guess the general implementation idea:

Use a  setter- like function to return as the second array item in the hook function, and the setter will control the state managed by the hook (State), and the state is returned by the first array item.

We can understand that there are two arrays, which respectively store methods corresponding to state and setState.

When useState () is run for the first time, the setter function is pushed to the setter array, and the state is pushed to the state array. Each setter has a reference to its cursor position, so by triggering a call to any setter, it will change the state value at that position in the state array. To put it plainly, there is an index, and the setter method modifies the corresponding state data value according to the index. Let's take a look at the implementation of pseudocode:


let state = []; // 存放state状态数据
let setter = []; // 存放 setXXX方法
let initial = true; // 是否是第一次运行
let index = 0;


useState(initVal) {
  if (initial) {
    state.push(initVal);
    setter.push(createSetter(index));
    initial = false;
  }

  const setter = setter[index];
  const value = state[index];

  index++;
  return [value, setter];
}


createSetter(index) {
  return function setterWithIndex(newVal) {
    state[index] = newVal;
  };
}

The specific source code implementation, everyone who is interested can go and see. However, it is not recommended to understand every step, just understand the realization idea.

to sum up

The state and related processing logic can be divided according to function, without having to be scattered in each life cycle, which greatly reduces the difficulty of development and maintenance. In addition to these hooks, there are other additional hooks: Hooks API Reference

Finally, recommend two very good libraries:

react-use Encapsulate various Hooks.

eslint-plugin-react-hooksHooks ESLint 插件

A very good React Native Hooks article written by a foreigner: React Hooks Basics— Building a React Native App with React Hooks

 

Published 214 original articles · praised 371 · 920,000 views

Guess you like

Origin blog.csdn.net/u013718120/article/details/88535116