React Hooks !!!!! Here is my github/blog address, if you can help, appreciate a star ~

🥺 learn how to use Typescript and React hooks from brain to brain. ✨

Know before class

🌸 I think a better way to learn is to follow the content of their own implementation again, so the first start a project bai ~

npx create-react-app hook-ts-demo --template typescript
Copy the code

Reference our case component in SRC/app.tsx and write our case component in SRC /example.tsx.

🌸 Use of functional components ~ We can use type-constrained functional components in the following ways:

import React from 'react'

type UserInfo = {
  name: string,
  age: number,
}

export const User = ({ name, age }: UserInfo) = > {
  return (
    <div className="App">
      <p>{ name }</p>
      <p>{ age }</p>
    </div>)}const user = <User name='vortesnail' age={25} />
Copy the code

Functional components with type constraints can also be used in the following ways:

import React from 'react'

type UserInfo = {
  name: string,
  age: number,
}

export const User:React.FC<UserInfo> = ({ name, age }) = > {
  return (
    <div className="User">
      <p>{ name }</p>
      <p>{ age }</p>
    </div>)}const user = <User name='vortesnail' age={25} />
Copy the code

The differences in the above code are:

export const User = ({ name, age }: UserInfo) = > {}
export const User:React.FC<UserInfo> = ({ name, age }) = > {}
Copy the code

To use a Functional Component, you need to declare the Component to be of type React.FC. In addition, the props need to declare the types of each parameter, which are then passed to React.

Although both approaches are similar, I personally prefer to use the React.FC approach to create my type-constrained functional component, which also supports children even though it is not defined in our type:

export const User:React.FC<UserInfo> = ({ name, age, children }) = > {
  return (
    <div className="User">
      <p>{ name }</p>
      <p>{ age }</p>
      <div>
        { children }
      </div>
    </div>)}const user = <User name='vortesnail' age={25}>I am children text!</User>
Copy the code

Nor do we need to explicitly deconstruct all the parameters:

export const User:React.FC<UserInfo> = (props) = > {
  return (
    <div className="User">
      <p>{ props.name }</p>
      <p>{ props.age }</p>
      <div>} {props. Children}</div>
    </div>)}const user = <User name='vortesnail' age={25}>I am children text!</User>
Copy the code

Ok, now that we know this much, we can start using our hooks

I’ll explain how to use our hooks in conjunction with typescript in three ways:

  • Why use ❓
  • How to use 🛠
  • Example: 📖

useState

Why use useState?

You can have functional components with state management features similar to this.state and this.setState in class components, but much cleaner and less frequent use of this.

How to use useState?

const [count, setCount] = useState<number> (0)
Copy the code

Scenario, for example,

1. General usage when parameters are basic types:
import React, { useState } from 'react'

const Counter:React.FC<{ initial: number }> = ({ initial = 0 }) = > {
  const [count, setCount] = useState<number>(initial)

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={()= >> add setCount (count + 1)}</button>
      <button onClick={()= >SetCount (count - 1)} ></button>
    </div>)}export default Counter
Copy the code
2. When the parameter is an object type:
import React, { useState } from 'react'

type ArticleInfo = {
  title: string,
  content: string
}

const Article:React.FC<ArticleInfo> = ({ title, content }) = > {
  const [article, setArticle] = useState<ArticleInfo>({ title, content })

  return (
    <div>
      <p>Title: { article.title }</p>
      <section>{ article.content }</section>
      <button onClick={()= >SetArticle ({title: 'next ', content:' next ',})}> next</button>
    </div>)}export default Article
Copy the code

SetXxx does not merge the old state as this.setState does. It replaces the old state completely.

setArticle({
  title: 'Next'.content: 'Next chapter'. article })Copy the code

useEffect

Why useEffect?

You can think of useEffect as a combination of componentDidMount, componentDidUpdate, and componentWillUnmount.

How do I use useEffect?

useEffect((a)= >{... return () => {... }}, [...]. )Copy the code

Scenario, for example,

1. Re-execute each time the state changesuseEffectThe logic of:
import React, { useState, useEffect } from 'react'

let switchCount: number = 0

const User = (a)= > {
  const [name, setName] = useState<string>(' ')
  useEffect((a)= > {
    switchCount += 1
  })

  return (
    <div>
      <p>Current Name: { name }</p>
      <p>switchCount: { switchCount }</p>
      <button onClick={()= > setName('Jack')}>Jack</button>
      <button onClick={()= > setName('Marry')}>Marry</button>
    </div>)}export default User
Copy the code
2. Perform the first operation even if the status changes each timeuseEffectThe logic of:
useEffect((a)= > {
  switchCount += 1
}, [])
Copy the code
3. Decide whether to re-execute according to whether a certain state has changed:
const [value, setValue] = useState<string>('I never change')
useEffect((a)= > {
  switchCount += 1
}, [value])
Copy the code

Value is not going anywhere to change its value, so after [value] is added to the end, the useEffect logic will only execute once, which is equal to executing componentDidMount in the class component, The subsequent shouldComponentUpdate returns all false.

4. Handle some memory problems during component uninstallation, such as clearing timer, clearing event listener:
useEffect((a)= > {
  const handler = (a)= > {
    document.title = Math.random().toString()
  }

  window.addEventListener('resize', handler)

  return (a)= > {
    window.removeEventListener('resize', handler)
  }
}, [])
Copy the code

useRef

Why useRef?

It’s not just for managing DOM refs, it’s the equivalent of this, and can hold any variable, which is a great way to avoid the inconvenience of closures.

How to use useRef?

const [count, setCount] = useState<number>(0)
const countRef = useRef<number>(count)
Copy the code

Scenario, for example,

1. Closure problem:

If we hit the Add button three times, then hit the popup box once, and then hit the add button twice, what would happen?

import React, { useState, useEffect, useRef } from 'react'

const Counter = (a)= > {
  const [count, setCount] = useState<number>(0)

  const handleCount = (a)= > {
    setTimeout((a)= > {
      alert('current count: ' + count)
    }, 3000);
  }

  return (
    <div>
      <p>current count: { count }</p>
      <button onClick={()= >> add setCount (count + 1)}</button>
      <button onClick={()= >HandleCount ()}> Popbox display</button>
    </div>)}export default Counter
Copy the code

The result is a popbox with current count: 3. Why?

When we update the state, React will re-render the component, getting a separate count state for each render, and re-render a handleCount function. Each handleCount has its own count inside it.

** How to display the latest current count?

const Counter = (a)= > {
  const [count, setCount] = useState<number>(0)
  const countRef = useRef<number>(count)

  useEffect((a)= > {
    countRef.current = count
  })

  const handleCount = (a)= > {
    setTimeout((a)= > {
      alert('current count: ' + countRef.current)
    }, 3000);
  }

  / /...
}

export default Counter
Copy the code
2. Because of change.currentProperty does not cause the component to rerender. This property allows us to get the previous value of the state:
const Counter = (a)= > {
  const [count, setCount] = useState<number>(0)
  const preCountRef = useRef<number>(count)

  useEffect((a)= > {
    preCountRef.current = count
  })

  return (
    <div>
      <p>pre count: { preCountRef.current }</p>
      <p>current count: { count }</p>
      <button onClick={()= >> add setCount (count + 1)}</button>
    </div>)}Copy the code

We can see that the previous value of the state is always displayed:

3. Manipulate Dom nodes, similar to createRef() :
import React, { useRef } from 'react'

const TextInput = (a)= > {
  const inputEl = useRef<HTMLInputElement>(null)

  const onFocusClick = (a)= > {
    if(inputEl && inputEl.current) {
      inputEl.current.focus()
    } 
  }

  return (
    <div>
      <input type="text" ref={inputEl}/>
      <button onClick={onFocusClick}>Focus the input</button>
    </div>
  )
}

export default TextInput
Copy the code

useMemo

Why useMemo?

You know from useEffect that you can influence the execution of certain functions by passing parameters to it. React checks to see if these parameters have changed, and does so only if there is a difference.

UseMemo does something similar, assuming there are a large number of methods and you only want to run them when their parameters change, not every time a component is updated, you can use useMemo for performance optimization.

Remember that functions passed into useMemo are executed during rendering. Please do not perform non-rendering operations inside this function. Operations such as side effects are used by useEffect, not useMemo.

How do I use useMemo?

function changeName(name) {
  return name + 'Do something to name and return a new name'
}

const newName = useMemo((a)= > {
	return changeName(name)
}, [name])
Copy the code

Scenario, for example,

1. Use it routinely to avoid repeating unnecessary methods:

Let’s start with a very simple example. Here’s the code that doesn’t use useMemo yet:

import React, { useState, useMemo } from 'react'

/ / the parent component
const Example = (a)= > {
  const [time, setTime] = useState<number>(0)
  const [random, setRandom] = useState<number>(0)

  return (
    <div>
      <button onClick={()= >SetTime (new Date().getTime())}> Get the current time</button>
      <button onClick={()= >SetRandom (math.random ())}> Gets the current random number</button>
      <Show time={time}>{random}</Show>
    </div>
  )
}

type Data = {
  time: number
}

/ / child component
const Show:React.FC<Data> = ({ time, children }) = > {
  function changeTime(time: number) :string {
    console.log('changeTime excuted... ')
    return new Date(time).toISOString()
  }

  return (
    <div>
      <p>Time is: { changeTime(time) }</p>
      <p>Random is: { children }</p>
    </div>)}export default Example
Copy the code

In this example, the changeTime method in the
component executes whether you click on the get current time button or the get current random number button.

In fact, clicking on the get current random number button will only change the children parameter, but our changeTime will also be re-executed due to the re-rendering of the child component, which is unnecessary and consumes irrelevant performance.

Modify our
subcomponent with useMemo:

const Show:React.FC<Data> = ({ time, children }) = > {
  function changeTime(time: number) :string {
    console.log('changeTime excuted... ')
    return new Date(time).toISOString()
  }

  const newTime: string = useMemo((a)= > {
    return changeTime(time)
  }, [time])

  return (
    <div>
      <p>Time is: { newTime }</p>
      <p>Random is: { children }</p>
    </div>)}Copy the code

The changeTime function will be executed only if you click on the current time, and clicking on the current random number will no longer trigger the function.

2. You may wonder,useMemoWhat can be done can’t be useduseEffectTo do?

The answer is no! If you add the following code to a child component:

const Show:React.FC<Data> = ({ time, children }) = > {
	/ /...
  
  useEffect((a)= > {
    console.log('effect function here... ')
  }, [time])

  const newTime: string = useMemo((a)= > {
    return changeTime(time)
  }, [time])
  
	/ /...
}
Copy the code

You’ll notice that the console will print the following:

> changeTime excuted...
> effect function here...
Copy the code

As we said at the beginning: functions passed into useMemo are executed during rendering. Here we have to mention react. memo, which implements the Pure function of the entire component:

const Show:React.FC<Data> = React.memo(({ time, children }) = >{... }Copy the code

The difference between useMemo and React. Memo is that useMemo in some cases does not want the component to perform shallow comparisons to all props, but only to implement local Pure functions, that is, only to compare specific props and decide whether to update locally or not.

useCallback

Why use useCallback?

Both useMemo and useCallback receive the same parameters. They are executed after their dependencies change and return cached values. The difference is that useMemo returns the result of a function run, while useCallback returns a function.

UseCallback (fn, deps) equivalent to useMemo(() => fn, deps)

How to use useCallback?

function changeName(name) {
  return name + 'Do something to name and return a new name'
}

const getNewName = useMemo((a)= > {
  return changeName(name)
}, [name])
Copy the code

Scenario, for example,

Change the previous useMemo example to the following component:

const Show:React.FC<Data> = ({ time, children }) = > {
  / /...
  const getNewTime = useCallback((a)= > {
    return changeTime(time)
  }, [time])

  return (
    <div>
      <p>Time is: { getNewTime() }</p>
      <p>Random is: { children }</p>
    </div>)}Copy the code

useReducer

Why use useReducer?

Ever wonder what it would look like if you wrote a lot of useState in one component? For example:

const [name, setName] = useState<string>(' ')
const [islogin, setIsLogin] = useState<boolean>(false)
const [avatar, setAvatar] = useState<string>(' ')
const [age, setAge] = useState<number>(0)
/ /...
Copy the code

How to use useReducer?

import React, { useState, useReducer } from 'react'

type StateType = {
  count: number
}

type ActionType = {
  type: 'reset' | 'decrement' | 'increment'
}

const initialState = { count: 0 }

function reducer(state: StateType, action: ActionType) {
  switch (action.type) {
    case 'reset':
      return initialState
    case 'increment':
      return { count: state.count + 1 }
    case 'decrement':
      return { count: state.count - 1 }
    default:
      return state
  }
}

function Counter({ initialCount = 0}) {
  const [state, dispatch] = useReducer(reducer, { count: initialCount })

  return (
    <div>
      Count: {state.count}
      <button onClick={()= > dispatch({ type: 'reset' })}>Reset</button>
      <button onClick={()= > dispatch({ type: 'increment' })}>+</button>
      <button onClick={()= > dispatch({ type: 'decrement' })}>-</button>
    </div>)}export default Counter
Copy the code

Scenario Example:

Combined with useContext to replace the Redux scheme, read on.

useContext

Why useContext?

Simply put, Context is a technique for providing globally shared data to the tree of components it contains.

How do I use useContext?

export const ColorContext = React.createContext({ color: '#1890ff' })
const { color } = useContext(ColorContext)
/ / or
export const ColorContext = React.createContext(null)
<ColorContext.Provider value='#1890ff'>
  <App />
</ColorContext.Provider>Const color = useContext(ColorContext) // #1890ff'Copy the code

Scenario, for example,

1. Root component registration, all child components can get the registered value:
import React, { useContext } from 'react'

const ColorContext = React.createContext<string>(' ')

const App = (a)= > {
  return (
    <ColorContext.Provider value='#1890ff'>
      <Father />
    </ColorContext.Provider>
  )
}

const Father = () => {
  return (
    <Child />
  )
}

const Child = () => {
  const color = useContext(ColorContext)
  return (
    <div style={{ backgroundColor: color}} >Background color is: { color }</div>
  )
}

export default App
Copy the code
2. Cooperate withuseReducerAlternatives to implementing Redux:
import React, { useReducer, useContext } from 'react'

const UPDATE_COLOR = 'UPDATE_COLOR'

type StateType = {
  color: string
}

type ActionType = {
  type: string,
  color: string
}

type MixStateAndDispatch = {
  state: StateType, dispatch? : React.Dispatch<ActionType> }const reducer = (state: StateType, action: ActionType) = > {
  switch(action.type) {
    case UPDATE_COLOR:
      return { color: action.color }
    default:
      return state  
  }
}

const ColorContext = React.createContext<MixStateAndDispatch>({
  state: { color: 'black'}})const Show = (a)= > {
  const { state, dispatch } = useContext(ColorContext)
  return (
    <div style={{ color: state.color}} >The current font color is {state.color}.<button onClick={()= >Dispatch && Dispatch ({type: UPDATE_COLOR, color: 'red'})}> red</button>
      <button onClick={()= >Dispatch && Dispatch ({type: UPDATE_COLOR, color: 'green'})}> green</button>
    </div>)}const Example = ({ initialColor = '# 000000' }) = > {
  const [state, dispatch] = useReducer(reducer, { color: initialColor })
  return (
    <ColorContext.Provider value={{state, dispatch}} >
      <div>
        <Show />
        <button onClick={()= >Dispatch && Dispatch ({type: UPDATE_COLOR, color: 'blue'})}> blue</button>
        <button onClick={()= >Dispatch && Dispatch ({type: UPDATE_COLOR, color: 'lightblue'})}> Light green</button>
      </div>
    </ColorContext.Provider>
  )
}

export default Example
Copy the code

This scheme is worth thinking about, especially with TypeScript type constraints! Of course, if there is a better solution, I hope there is a big man to put forward, I can also learn more ~

conclusion

I have read a lot of good articles recently. Thanks to the selfless dedication of the nuggets, this article is inspired by one of the most popular articles recently:

React Hooks: !!!!!

This article is easy to read, but it doesn’t cover Typescript usage, and I couldn’t find a similar in-door article on Nuggets, so I decided to write it myself, hoping to help some friends and make up for my own knowledge.

React-hooks: !!!!! Redux React Hooks Tutorial on pure useReducer using useContext + useReducer.

Build a React + Typescript development environment from scratch. This is a series of articles on how to build a React + Typescript development environment from scratch. It is one of the most clear and excellent articles I have seen.

You can see your muscles clearly and formatter using react + typescript from scratch. webpack