React Hooks Deep Series - Design Mode

Keywords: PHP React TypeScript Vue

This article is React Hooks in-depth series Follow-up.This article details the advantages of Hooks over classes, introduces the design ideas of related api, and explains how Hooks align the life cycle hooks of classes.

React Logo and Hooks

React's logo is an atomic pattern in which atoms make up the representation of matter.Similarly, React is an atom that makes up the page; Hooks, like quark, are closer to what React is like, but were not really designed until four years later.- Dan in React Conf(2018)

why Hooks?

1: Logical reuse among multiple components: React cannot separate logic with state into function s in Class, it can only solve the problem of logical reuse among multiple components by nested components. The idea based on nested components exists HOC And render props.But are there any drawbacks in these two design modes?

  • Nested Hell, when there are too many levels of nesting, tracing data sources can become difficult, making it difficult to locate bug s; (hoc, render props)
  • Performance requires additional component instances with additional overhead; (hoc, render props)
  • Naming repetition, the use of multiple HOCs in one component at the same time, does not exclude the problem of naming conflicts among the methods in these hocs; (hoc)

2. Logical reuse in a single component: Most of the logic in a Class's life cycle componentDidMount, componentDidUpdate, or even componentWillUnMount is basically similar. It is not user-friendly to have to break up the logic to maintain the same in different life cycles, which also results in an increase in the amount of code in the component.

3: Other issues with Class: There are a lot of templates to write about using Class in React, and users are often confused with the use of Constructor's bind and this in Classes; when using Class in conjunction with TypeScript, additional declaration processing is required for defaultValue; in addition, React Team indicates that Class is machine-based.Optimized aspects of compilation are also not ideal.

What is the value returned by useState as an array instead of an object?

The reason is that array deconstruction is more convenient than object deconstruction, so you can observe the differences between the two data structures.

When the array is returned, any name can be resolved directly.

[name, setName] = useState('Monkey D Luffy')
[age, setAge] = useState(12)

When an object is returned, an additional layer of naming is required.

{value: name, setValue: setName} = useState('Monkey D Luffy')
{value: name, setValue: setName} = useState(12)

Design of Hooks Delivery

Can Hooks be designed to be used in components by function arguments, such as making the following calls?

const SomeContext = require('./SomeContext)

function Example({ someProp }, hooks) {
  const contextValue = hooks.useContext(SomeContext)
  return <div>{someProp}{contextValue}</div>
}

The disadvantage of using pass is redundant pass.(You can associate what context solves)

Does Hooks behave differently from calls to setState in lass?

The biggest difference between setStates in Hooks and lass is that Hooks does not merge multiple setStates.If you want to perform a merge operation, you can do the following:

setState(prevState => {
  return { ...prevState, ...updateValues }
})

In addition, you can compare whether setState behaves asynchronously or synchronously between class es and Hooks by first observing and analyzing the number of render outputs in the following four scenarios:

Can React Hooks be used instead of Redux

After React 16.8, for business scenarios that are not particularly complex, you can use the useContext and useReducer provided by React to implement a customized simplified version of redux, which is visible todoList Use in.The core code is as follows:

import React, { createContext, useContext, useReducer } from "react"

// Create StoreContext
const StoreContext = createContext()

// Build Provider Container Layer
export const StoreProvider = ({reducer, initialState, children}) => {
  return (
    <StoreContext.Provider value={useReducer(reducer, initialState)}>
      {children}
    </StoreContext.Provider>
  )
}

// Call useStoreContext in the subcomponent to get the value in Provider
export const useStoreContext = () => useContext(StoreContext)

However, this mode is not currently recommended for particularly complex scenarios, because context s have performance problems with their mechanisms.Specific reasons are visible Reason for react-redux v7 fallback to subscription

How to get previous props and state in Hooks

React officially will likely provide a hooks for usePrevious in the future to get previous props and state s.

The core idea of usePrevious is to use ref to store previous values.

function usePrevous(value) {
  const ref = useRef()
  useEffect(() => {
    ref.current = value
  })
  return ref.current
}

How to call methods on instances in Hooks

Using useRef() in Hooks is equivalent to using this.something in Class.

/* in a function */
const X = useRef()
X.current // can read or write

/* in a Class */
this.X    // can read or write

Is there something like instance variables

An alternative to getDerivedStateFromProps in Hooks

stay React Darkware It is mentioned that getDerivedStateFromProps is an anti-pattern, but in rare cases this hook is still used. Hooks does not have this api, how can it achieve the effect of getDerivedStateFromProps?

function ScrollView({row}) {
  const [isScrollingDown, setISScrollingDown] = setState(false)
  const [prevRow, setPrevRow] = setState(null)

  // The core is to create a prevRow state to compare with the row s passed in from the parent component
  if (row !== prevRow) {
    setISScrollingDown(prevRow !== null && row > prevRow)
    setPrevRow(row)
  }

  return `Scrolling down ${isScrollingDown}`
}

Alternative to forceUpdate in Hooks

You can use useReducer to hack forceUpdate, but try to avoid using forceUpdate.

const [ignored, forceUpdate] = useReduce(x => x + 1, 0)

function handleClick() {
  forceUpdate()
}

Alternative to shouldComponentUpdate in Hooks

UseMemo can be used as an alternative to shouldComponentUpdate in Hooks, but useMemo only makes a shallow comparison of props.

React.useMemo((props) => {
  // your component
})

The difference between useMemo and useCallback

useMemo(() => <component />) Equivalent to useCallback(<component />)
  • useCallback: Typically used for caching functions
  • useMemo: Typically used for caching components

Is it safe to remove a function from a dependency list?

It is generally unsafe to remove a function from a dependency list.Watch demo below

const { useState, useEffect } = React

function Example({ someProp }) {
  function doSomething() {
    console.log(someProp) // Only 1 is output here, and 2 clicking on the button does not.
  }

  useEffect(
    () => {
      doSomething()
    },
    [] // (viii) This is not safe because someProps property is used in the doSomething function
  )

  return <div>example</div>
}

export default function() {
  const [value, setValue] = useState(1)
  return (
    <>
      <Example someProp={value} />
      <Button onClick={() => setValue(2)}>button</Button>
    </>
  )
}

In this demo, click the button button and it does not print out 2.There are two ways to solve these problems.

Method one: Put the function in useEffect and the related attributes in the dependency.This is also the preferred practice because the related attributes that change in dependency are clear at a glance.

function Example({ someProp }) {
  useEffect(
    () => {
      function doSomething() {
        console.log(someProp)
      }
      doSomething()
    },
    [someProps] // Related property changes are clear at a glance
  )

  return <div>example</div>
}

Method 2: Add a function to the dependency list

function Example({ someProp }) {
  function doSomething() {
    console.log(someProp)
  }

  useEffect(
    () => {
      doSomething()
    },
    [doSomething]
  )

  return <div>example</div>
}

Scenario 2 is rarely used alone; it is generally used in conjunction with useCallback to handle functions that require a lot of computation.

function Example({ someProp }) {
  const doSomething = useCallback(() => {
    console.log(someProp)
  }, [someProp])

  useEffect(
    doSomething(),
    [doSomething]
  )

  return <div>example</div>
}

How to avoid creating expensive objects repeatedly

  • Method 1: Use the lazy initialization of useState as follows
const [value, setValue] = useState(() => createExpensiveObj)

see lazy-initial-state;

  • Method 2: Use custom useRef function
function Image(props) {
  const ref = useRef(null)

  function getExpensiveObj() {
    if (ref.current === null) {
      ref.current = ExpensiveObj
    }

    return ref.current
  }

  // if need ExpensiveObj, call getExpensiveObj()
}

Related Data

Posted by Unknown_Striker on Wed, 31 Jul 2019 14:44:19 -0700