In layman's language React Hooks

Live replay link: Yunqi Community ( @-the X-Cold )

What React Hooks that?

Hooks As the name suggests, the literal sense is the concept React hook. Through a case we first have a first impression React Hooks.

Suppose now to implement components of a counter. When a component of the way, we need to do relatively more, such as declarations state, ways of writing a counter, etc., and may be more concept to understand some of the concepts such as Javascript class, this context points, etc. .

Examples

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Counter extends React.Component {
  state = {
  	count: 0
  }

  countUp = () => {
    const { count } = this.state;
  	this.setState({ count: count + 1 });
  }
  
  countDown = () => {
    const { count } = this.state;
  	this.setState({ count: count - 1 });
  }
  
  render() {
    const { count } = this.state;
  	return (
      <div>
        <button onClick={this.countUp}>+</button>
        <h1>{count}</h1>
        <button onClick={this.countDown}>-</button>
      </div>
    )
  }
}

ReactDOM.render(<Counter />, document.getElementById('root'));
复制代码

Use React Hooks, so we can write.

Examples

import React, { useState } from 'react';
import ReactDOM from 'react-dom';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>+</button>
      <h1>{count}</h1>
      <button onClick={() => setCount(count - 1)}>-</button>
    </div>
  )
}

ReactDOM.render(<Counter />, document.getElementById('root'));
复制代码

By way of example above, it is obvious React Hooks provide a simple, functional style program (FP), and to interact (the MVVM) state to the UI by the pure components and functions controlled data stream.

Hooks API

useState

useState is the most basic API, which passed an initial value every time the function execution can get the new value.

import React, { useState } from 'react';
import ReactDOM from 'react-dom';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>+</button>
      <h1>{count}</h1>
      <button onClick={() => setCount(count - 1)}>-</button>
    </div>
  )
}

ReactDOM.render(<Counter />, document.getElementById('root'));
复制代码

It should be noted, by state useState get the count, performance in Counter component is a constant, after each modified by setCount, again by useState to get a new constant.

useReducer

useReducer useState and almost the same, the reducer requires external external (global), multiple states can be controlled simultaneously in this manner. A closer look it, in fact with the concept of data streams redux is very close.

import { useState, useReducer } from 'react';
import ReactDOM from 'react-dom';

function reducer(state, action) {
  switch (action.type) {
    case 'up':
      return { count: state.count + 1 };
    case 'down':
      return { count: state.count - 1 };
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 1 })
  return (
    <div>
      {state.count}
      <button onClick={() => dispatch({ type: 'up' })}>+</button>
      <button onClick={() => dispatch({ type: 'down' })}>+</button>
    </div>
  );
}

ReactDOM.render(<Counter />, document.getElementById('root'));
复制代码

useEffect

A crucial Hooks API, as the name suggests, useEffect is used to treat side effects caused by changes in various states, meaning that only at a particular time, logic will be executed.

import { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';

function Example() {
  const [count, setCount] = useState(0);

  // => componentDidMount/componentDidUpdate
  useEffect(() => {
    // update 
    document.title = `You clicked ${count} times`;
    // => componentWillUnMount
    return function cleanup() {
    	document.title = 'app';
    }
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'));
复制代码

useMemo

useMemo mainly used to optimize the rendering process, two parameters are calculated sequentially function (typically a function of the component) and dependent status list, when dependent changes state to trigger the execution of the calculation function. If you do not specify a dependency, then each time the rendering process will perform this calculation functions.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
复制代码
import { useState, useMemo } from 'react';
import ReactDOM from 'react-dom';

function Time() {
	return <p>{Date.now()}</p>;
}

function Counter() {
  const [count, setCount] = useState(0);
  
  const memoizedChildComponent = useMemo((count) => {
    return <Time />;
  }, [count]);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
      <div>{memoizedChildComponent}</div>
    </div>
  );
}

ReactDOM.render(<Counter />, document.getElementById('root'));
复制代码

useContext

context is externally create, internal use of the state, and it is the difference between global variables, if multiple components simultaneously useContext, then these components will rerender, if multiple components simultaneously useState same global variable, only the trigger of the current component setState rerender.

Example - not used useContext

import { useState, useContext, createContext } from 'react';
import ReactDOM from 'react-dom';

// 1. 使用 createContext 创建上下文
const UserContext = new createContext();

// 2. 创建 Provider
const UserProvider = props => {
  let [username, handleChangeUsername] = useState('');
  return (
    <UserContext.Provider value={{ username, handleChangeUsername }}>
      {props.children}
    </UserContext.Provider>
  );
};

// 3. 创建 Consumer
const UserConsumer = UserContext.Consumer;

// 4. 使用 Consumer 包裹组件
const Pannel = () => (
  <UserConsumer>
    {({ username, handleChangeUsername }) => (
      <div>
        <div>user: {username}</div>
        <input onChange={e => handleChangeUsername(e.target.value)} />
      </div>
    )}
  </UserConsumer>
);

const Form = () => <Pannel />;

const App = () => (
  <div>
    <UserProvider>
      <Form />
    </UserProvider>
  </div>
);

ReactDOM.render(<App />, document.getElementById('root'));
复制代码

Example - Using useContext

import { useState, useContext, createContext } from 'react';
import ReactDOM from 'react-dom';

// 1. 使用 createContext 创建上下文
const UserContext = new createContext();

// 2. 创建 Provider
const UserProvider = props => {
  let [username, handleChangeUsername] = useState('');
  return (
    <UserContext.Provider value={{ username, handleChangeUsername }}>
      {props.children}
    </UserContext.Provider>
  );
};

const Pannel = () => {
  const { username, handleChangeUsername } = useContext(UserContext); // 3. 使用 Context
  return (
    <div>
      <div>user: {username}</div>
      <input onChange={e => handleChangeUsername(e.target.value)} />
    </div>
  );
};

const Form = () => <Pannel />;

const App = () => (
  <div>
    <UserProvider>
      <Form />
    </UserProvider>
  </div>
);

ReactDOM.render(<App />, document.getElementById('root'));
复制代码

useRef

useRef ref returns a variable object which .current property parameters passed initialized (initialValue). The returned object will last the entire life cycle of components. In fact useRef is a very useful API, in many cases, we need to keep some things change, it will come in handy in.

Examples

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
复制代码

React state sharing scheme

Speaking state sharing, the simplest and most direct way is to cascade through props transmission state, this assembly coupled to a parent-child relationship, once the nested assembly configuration changes, it is necessary to re-write the code, the maintenance cost is very expensive. Over time, the official introduced a variety of programs to address the status code reuse and sharing of problems.

Mixins


React, only components created by createClass to use mixins. This high coupling dependence is difficult to control, as the wave of high complexity way ES6 gradually fade out the stage of history.

HOC

Higher order components from functional programming, because of React components may be considered as a function (s), thus can naturally be implemented by code reuse HOC manner. Properties may be achieved by agents and reverse succession, HOC can easily control the rendering result may be operated assembly props / state, so that the complex can easily reuse code logic.

import React from 'react';
import PropTypes from 'prop-types';

// 属性代理
class Show extends React.Component {
  static propTypes = {
    children: PropTypes.element,
    visible: PropTypes.bool,
  };

  render() {
    const { visible, children } = this.props;
    return visible ? children : null;
  }
}

// 反向继承
function Show2(WrappedComponent) {
  return class extends WrappedComponent {
    render() {
      if (this.props.visible === false) {
        return null;
      } else {
        return super.render();
      }
    }
  }
}

function App() {
	return (
  	<Show visible={Math.random() > 0.5}>hello</Show>
  );
}
复制代码

Redux state in multiplexing HOC is a typical implementation, we can be assembled to compose the data through the target component, of course, you can also be treated by the decorator manner.

import React from 'react';
import { connect } from 'react-redux';

// use decorator
@connect(state => ({ name: state.user.name }))
class App extends React.Component{
  render() {
		return <div>hello, {this.props.name}</div>
  }
}

// use compose
connect((state) => ({ name: state.user.name }))(App);
复制代码

Render Props

Apparent, renderProps A is transferred to the render method as props subassembly embodiment, compared with the HOC embodiment, renderProps protect existing component hierarchy.

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';

// 与 HOC 不同,我们可以使用具有 render prop 的普通组件来共享代码
class Mouse extends React.Component {
  static propTypes = {
    render: PropTypes.func.isRequired
  }

  state = { x: 0, y: 0 };

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

function App() {
  return (
    <div style={{ height: '100%' }}>
      <Mouse render={({ x, y }) => (
          // render prop 给了我们所需要的 state 来渲染我们想要的
          <h1>The mouse position is ({x}, {y})</h1>
        )}/>
    </div>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'));
复制代码

Hooks

Hooks API and by a combination of built-React Context, from the previous example can be seen by Hook state between the component sharing allows clearer and simpler.

React Hooks design

Fundamental

function FunctionalComponent () {
  const [state1, setState1] = useState(1);
  const [state2, setState2] = useState(2);
  const [state3, setState3] = useState(3);
}
复制代码

{
  memoizedState: 'foo',
  next: {
    memoizedState: 'bar',
    next: {
      memoizedState: 'bar',
      next: null
    }
  }
}
复制代码

Functional implement in the end

capture props

Function component is inherently support props, there is no much difference on the basic usage and class components. Note that the difference between the two:

  • class mount assembly props this context, the functional assembly by parameter passing;
  • Due to differences in position mount, class components if this changes, then this.props will change; but always in the function components in props are immutable, and therefore comply with the principle of capture value (ie acquisition value is always at a certain time), Hooks also follow this principle.

Through an example to understand how the capture value, we can evade capture value through useRef, because useRef is variable.

state

class components Function Component
Creating state this.state = {} useState, useReducer
Modify status this.setState() set function
Update mechanism Asynchronous update, merge several revisions to the state, to produce a copy of the Updated simultaneously, directly modifying the target state
State Management A plurality of centralized management state status A plurality of state, the state can be combined (manually) by useReducer
performance high If useState initialization state need to get through a very complex calculations, please use declarative function, otherwise render repeats itself every time

The life cycle

  • componentDidMount / componentDidUpdate / componentWillUnMount

useEffect is called every time the rendering will be, a little bit can be used as packaging these life cycle;

  • shouldComponentUpdate

Normally we optimize component performance, purely by way of priority to reduce the number of components render the individual components.

class Button extends React.PureComponent {}
复制代码

React Hooks useMemo may be employed in place, it can be achieved only when certain data re-render component changes, equivalent to the shouldComponentUpdate shallowEqual comes.

Forced rendering forceUpdate

As the default, each time modifying condition will cause the re-rendered, as may be set by forceUpdate a function not in use.

const forceUpdate = () => useState(0)[1];
复制代码

The principle

Based Hooks, Hooks enhanced

To a combination of boxing it!

Since the concept of each Hooks API functions are pure, more concerned about the input (input) and output (output) use, so you can function better by assembling a way, Hooks API on the basis of different characteristics are combined to create a new own Hooks characteristics.

  • useState maintenance component state
  • useEffect treatment side effects
  • useContext listening updated change provider

useDidMount

import { useEffect } from 'react';

const useDidMount = fn => useEffect(() => fn && fn(), []);

export default useDidMount;
复制代码

useDidUpdate

import { useEffect, useRef } from 'react';

const useDidUpdate = (fn, conditions) => {
  const didMoutRef = useRef(false);
  useEffect(() => {
    if (!didMoutRef.current) {
      didMoutRef.current = true;
      return;
    }
    // Cleanup effects when fn returns a function
    return fn && fn();
  }, conditions);
};

export default useDidUpdate
复制代码

useWillUnmount

When talking about useEffect already mentioned, which allows a cleanup function to return, the assembly will perform cleanup function when unmounted, and therefore can easily useWillUnmount ~

import { useEffect } from 'react';

const useWillUnmount = fn => useEffect(() => () => fn && fn(), []);

export default useWillUnmount;
复制代码

useHover

Examples

// lib/onHover.js
import { useState } from 'react';

const useHover = () => {
  const [hovered, set] = useState(false);
  return {
    hovered,
    bind: {
      onMouseEnter: () => set(true),
      onMouseLeave: () => set(false),
    },
  };
};

export default useHover;
复制代码
import { useHover } from './lib/onHover.js';

function Hover() {
  const { hovered, bind } = useHover();
  return (
    <div>
      <div {...bind}>
        hovered:
        {String(hovered)}
      </div>
    </div>
  );
}
复制代码

useField

Examples

// lib/useField.js

import { useState } from 'react';

const useField = (initial) => {
  const [value, set] = useState(initial);

  return {
    value,
    set,
    reset: () => set(initial),
    bind: {
      value,
      onChange: e => set(e.target.value),
    },
  };
}

export default useField;
复制代码
import { useField } from 'lib/useField';

function Input {
  const { value, bind } = useField('Type Here...');

  return (
    <div>
      input text:
      {value}
      <input type="text" {...bind} />
    </div>
  );
}

function Select() {
  const { value, bind } = useField('apple')
  return (
    <div>
      selected:
      {value}
      <select {...bind}>
        <option value="apple">apple</option>
        <option value="orange">orange</option>
      </select>
    </div>
  );
}
复制代码

Precautions

  • Hook scope of use: React the functional components, the custom function Hook Lane;
  • Hook function must be written in the outermost layer, which will change every time useState subscript (cursor), React to update the status in accordance with the order;
  • Although each rendering will be executed Hook API, but the state (state) is always generated by a constant (within the scope of the function);

Epilogue

React Hooks offer provides new possibilities for state management, although we may require additional to maintain some internal state, but the state administration to deal with problems can be avoided by renderProps / HOC other complex ways. Hooks brings benefits as follows:

  • More fine-grained code reuse, and without excessive side effects
  • Functional programming style, code more concise, while reducing the use and understanding threshold
  • Abatement assembly nesting
  • Component data flow more clearly

In fact, custom tailored Hooks under various scenarios, to make our application more convenient and simple, hierarchy of components can ensure the integrity, there is such a pleasant functional programming style, Hooks in React 16.8 .0 version has been officially released a stable version, now began to get up! ! !

Reference material

Guess you like

Origin blog.csdn.net/weixin_34255055/article/details/91381799