preface

This article introduces some practical uses and scenarios for React Hooks, which follow my own path of thinking (why-why-how).

What is the

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

Xia: What class can do is in which I use hooks. Give up the fight, boy.

React Hooks are a class of functions in functional components that begin with the name use. Functions like this are treated differently inside React, so they are also called hook functions.

  • Functional component

Hooks only work with Function Components, which is not strictly true. I prefer to say that it is recommended that Function Components only use Hooks

  • Use the beginning

React: The React convention uses the use prefix to name hooks, which makes it easy to identify them

  • Special treatment

Hooks, as Hooks, have an internal list of “memory locations” associated with each component. They’re just JavaScript objects that we can put some data on. When you call the Hook like useState (), it reads the current cell (or initializes it on the first render) and then moves the pointer to the next cell. This is how multiple useState () calls the separate local state of each GET

why

To solve the question of why hooks are used, I decided to address both the pain points that hooks address in the class component and the fact that hooks fit more closely with the React component model.

1. Doesn’t the class component smell good?

The class component is nice, but there are a number of problems exposed. Redux author Dan Abramov summed up a few pain points:

  • Huge components that are hard to refactor and test.
  • Duplicated logic between different components and lifecycle methods.
  • Complex patterns like render props and higher-order components.

First: Large components that are difficult to refactor and test. Wouldn’t you panic if you were asked to add a new feature to a 300-plus line of code component? Have you ever tried to comment out a line of code, and it didn’t run or make sense? If you need to introduce a Redux or timer, it’s even more scary

Second point: logical duplication between different components and lifecycle methods. This difficulty is no less difficult than the Shu road – difficult to heaven! Of course, simple logic can be solved by HOC and render props. However, these two solutions have two fatal drawbacks: complex and nested schemas.

Third: complex patterns, such as Render props and HOC. I have to say that when I was studying render props, I asked: Is the render props only if the render attribute is passed to it? As if I could pass in a function for any property (such as children) to achieve the same effect; When you first start using HOC, open React Collaborate Tools.

These three points can be fixed by Hooks.

2. Hooks are more consistent withReact programming model?

We know react emphasizes one-way data flow and data-driven views, which means components and top-down data flow can help us split up the UI and implement the page UI like building blocks. There is more emphasis on composition than nesting, and classes do not perfectly interpret this model, but hooks working with functional components do! Pure UI for functional components with Hooks to provide state and side effects that isolate components into logically reusable units.

How to do

Don’t ask, ask is the document, if not, please read and recite the document…

But (all things fear But), since it is practice, you have to pretend to practice, here is my simple practice and ideas.

1. Change your mental model

Mental models

  1. State a spindle
// in class component
class Demo extends React.Component {
 constructor(props) {
   super(props)
   this.state = {
     name: 'Hello',
     age: '18', rest: {}, } } ... } / /in function component
function Demo(props) {
 const initialState = {
   name: 'Hello',
   age: '18',
   rest: {},
 }
 const [state, setState] = React.useState(initialState)
 ...
}
Copy the code
  1. Try simulating the life cycle
// This implementation is crude and can work with useRef and useCallback, but even this is not completely equivalent to componentDidMountfunction useDidMount(handler){
  React.useEffect(()=>{
      handler && handler()
  }, [])
}
Copy the code
  1. Using setInterval in useEffect sometimes backfires
// count changes to 1function Counter() {
  const [count, setCount] = React.useState(0);
  useEffect(() => {
    let id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return() => clearInterval(id); } []); . }Copy the code

In fact, thinking in the class Component context is more like doing certain things at certain points in time. For example, we’ll initialize state in constructor, request data after DidMount, etc. Logic that handles state changes after component updates (DidUpdate) removes side effects before component unmounts (willUnmount)

However, in the context of hooks+function Component, thinking should be more oriented to specific function logic, and thinking in terms of function as a unit will feel suddenly enlightened. Such as changing the document title, network requests, timers… For hooks, it’s just a tool for a specific function

You will find that most of the specific features you want to implement have effects, it is safe to say that useEffect is the Hooks that most disturb your mental model, which are closer to state synchronization than to responding to life cycle events. Another thing that may affect you is that each render has its own resource, which is shown in the following ways

  • Each render has its own Props and State: React rerenders the component when we update the State. Each render gets a separate state value, which is a constant in the function (i.e. during any render, props and state are always the same).
  • Each render has its own event handlers: like props and state, they belong to a particular render, and even asynchronous handlers can only get the state of that particular render
  • Functions within each component (including event handlers, effects, timers, or API calls, etc.) capture props and state defined in a render. (It is recommended to always quantify props and State for each render when analyzing problems.)

2. What are called Hooks practice

UseState –Related states are put together

  • Instead of all states in one shuttle, you can write multiple usestates. The basic principle is to put related states together
  • SetXXX (XXX => XXX…)
  • To manage complex states consider using a useReducer(such as status updates that depend on the value of another state)
Const [count, const [count,setCount] = React.useState(0);
 set// Display user information const initialUser = {name:'Hello',
  age: '18',
}
const [user, setUser] = React.useState(initialUser)
Copy the code

UseEffect –Don’t accept the side effects of deception

  • Don’t lie to dependency arrays. All values in the components used in effect are included in the dependency. This includes props, state, functions, anything inside the component
  • Don’t abuse dependent array items and let Effect feed on itself
  • Effects are cleared by returning a function, and the last effects will not be cleared until the rendering is redone
// 修改上面count更新到1就不动了,方法1
function Counter() {
  const [count, setCount] = React.useState(0);
  useEffect(() => {
    let id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return() => clearInterval(id); }, [count]); . } // select count (1), count (1), count (1)function Counter() {
  const [count, setCount] = React.useState(0);
  useEffect(() => {
    let id = setInterval(() => {
      setCount(count => count + 1);
    }, 1000);
    return() => clearInterval(id); } []); . }Copy the code

For useEffect, Wall Split recommends Dan Abramov’s A Complete Guide to useEffect, an in-depth article on the structure of the entire article!

UseReducer –Powerful state management mechanism

  • Separating what happens in the component (actions) from how the state responds and updates
/** Change requirements: instead of adding a number per second, it is up to the user to decide how much to add, which can be seen as +step* instead of +1function Counter() {
  const [count, setCount] = React.useState(0);
  const [step, setStep] = React.useState(1);
  useEffect(() => {
    let id = setInterval(() => {
      setCount(count => count + step);
    }, 1000);
    return() => clearInterval(id); }, [step]); . } const initialState = {count: 0, step: 1,};function reducer(state, action) {
  const { count, step } = state;
  if (action.type === 'tick') {
    return { ...state, count: count + step };
  } else if (action.type === 'step') {
    return{... state, step: action.step }; }}function Counter() {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  const { count, step } = state;

  useEffect(() => {
    const id = setInterval(() => {
      dispatch({ type: 'tick' });
    }, 1000);
    return() => clearInterval(id); }, [dispatch]); . }Copy the code

UseCallback –FP is a good partner for using functions

Before I say that, if you’re going to use functions in FP, you have to think about what are the alternatives?

Option 1: If the function does not use any values inside the component, define it outside the component

Scheme 2: If the function is only used in effect, define it in effect

If there’s no alternative, it’s time to useCallback.

  • Return a Memoized callback, don’t lie about dependent arrays
// Scenario 1: Query dependent on componentsfunction Search() {
  const [query, setQuery] = React.useState('hello');
  
  const getFetchUrl = React.useCallback(() => {
    return`xxxx? query=${query}`; }, [query]); useEffect(() => { const url = getFetchUrl(); }, [getFetchUrl]); . } // Scenario 2: As propsfunction Search() {
   const [query, setQuery] = React.useState('hello');

  const getFetchUrl = React.useCallback(() => {
    return`xxxx? query=${query}`;
  }, [query]);  

  return <MySearch getFetchUrl={getFetchUrl} />
}

functionMySearch({ getFetchUrl }) { useEffect(() => { const url = getFetchUrl(); }, [getFetchUrl]); . }Copy the code

UseRef –Mutable container with memory function

  • Return a mutable ref container object whose.current property is initialized as the passed parameter (initialValue). The ref object returned remains constant throughout the life of the component, meaning that the same REF object is returned every time it is rendered
  • UseRef does not notify you when the ref object’s contents change. Changing the.current property does not cause component rerendering
  • You can store a “box” of variable values in the ref.current property. Store a handle to the real DOM/store an event listener/record the Function Component rendering value (eg: last state/props, timer ID….)
// Store the constant reference type const {current: StableArray} = react. useRef([1, 2, 3]) < comparr ={stableArray} /> const inputEl = useRef(null); <input ref={inputEl}type="text"Const savedCallback = useRef(); useEffect(() => { savedCallback.current = callback; }Copy the code

UseMemo –Record expensive values

  • Return an Memoized value, don’t lie about the dependency array
  • UseRef is preferred most of the time, and useMemo is often used to handle expensive computations
  • You can rely on useMemo for performance optimization, but not for semantic assurance (memory values may be forgotten in the future, for example to free memory)
Const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);Copy the code

UseContext –Powerful context

  • Receives a context (the return value of React. CreateContext) and returns the current value of the context, The current context value is determined by the value of the < myContext.provider value={value}> first rendered in the upper component
  • When the most recent

    update is made to the component’s upper layer, the Hook will trigger a rerender using the latest context value passed to MyContext Provider. If rerendering the component is too expensive, You can optimize it by using useMemo
Const themes = {foreground: {foreground: foreground;"# 000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "# 222222"}}; const ThemeContext = React.createContext(themes.light);function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}
Copy the code

eggs

Say egg, in fact, is a supplementary note ~~

1. An important oneThe rules(Code is not standard, two lines of tears)

In addition to starting with use, hooks are only allowed to be called at the top level of the react function.

Given that you’re in the spirit of Jing, you might ask, why not, if I wanted to? If I were hooks developer, I wouldn’t hesitate to say go out and turn right, bring on the next developer! Of course, it’s worth exploring if you want to know why. This rule ensures that hooks in the component are called in order. So why is order so important? Can’t you give every hooks a unique identifier, so they can do whatever they want? I thought about it all the time until Dan gave me the answer, simply for the best use of hooks — custom-hooks

2. custom-hooks

It strikes me that custom-hooks are a combination of things that really explain the magic of the React programming model. You can bet against it, but it does have something, or at least the idea that it presents makes me think about it more and more. So much so that Vue 3.0 took his lead and released Vue Hooks. React-conf 2018 custom-hooks are recommended.

// Change the page titlefunctionuseDocumentTitle(title) { useEffect (() => { document.title = title; }, [title]); } // Use the form inputfunction useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue);

  function handleChange(e) {
    setValue(e.target.value);
  }

  return {
    value,
    onChange: handleChange
  };
}
Copy the code

Write in the last

Finally, two trivia points for discussion.

  1. React Hooks have no disadvantages?

    • There must be, and the most intuitive experience for me is the closure that you both love and hate
    • Repeated rendering over and over introduces performance issues that require human optimization
  2. The setInterval code is a lot of code. Can you wrap it in custom-hooks?

    • 2. Making setInterval Declarative with React Hooks
    • If you have a bunch of hooks for specific functions, is it entirely possible to assemble hooks for business logic such as network requests, bind event listening, etc

My ability is limited, if there is any wrong place, welcome to criticize and correct!

Finally, in case you missed it, amway Dan Abramov’s A Complete Guide to useEffect is an in-depth article on the structure of this article.