current position:Home>Ten thousand words sum up react knowledge points to see if you can?

Ten thousand words sum up react knowledge points to see if you can?

2021-08-27 07:52:10 Jiaxin cansiny

This is my participation 8 The fourth of the yuegengwen challenge 3 God , Check out the activity details :8 Yuegengwen challenge

This article uses React Version is 17.0.2, Non special declarations use functional components by default

️ Environmental preparation

We use official scaffolding create-react-app To create our react application

First, make sure Node >= 10.16 and npm >= 5.6

node -v
npm -v
 Copy code 

And then execute

npx create-react-app react-demo
cd react-demo
npm start
 Copy code 

One React The project is created

JSX

JSX It's a JavaScript Grammar extension of , So that we can js Write in html、css...

const element = <h1>Hello, world!</h1>
 Copy code 

The above code is a simple JSX, We can also do that JSX Chinese expression , Use {} The parcel

const element = <h1>Today is {new Date().toLocaleDateString()}</h1>
 Copy code 

JSX The notes in the are also required {}, Of course, you can use it directly vscode Shortcut key Ctrl + /

 {/*  The right way to write notes  */}
 { //  The right way to write notes  }
 Copy code 

JSX It's also an expression , in other words , You can if Statement and for Use... In cyclic code blocks JSX, take JSX Assign a value to a variable , hold JSX Pass in as a parameter , And return... From the function JSX

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>
  }
  return <h1>Hello, Stranger.</h1>
}
 Copy code 

In addition to JSX Write in html Labeled attribute when , To use Naming of small hump , for example ,JSX Inside class Turned into className,onclick Turned into onClick

const element = <div className='classname'></div>
 Copy code 

When using inline styles , It's written in double brackets ( Use the little hump to write css style )

 <h1 style={{ fontSize: "16px" }}>{count}</h1>
 Copy code 

JSX Tags can contain many sub elements , Use () The parcel

const element = (
  <div> <h1>Hello!</h1> <h2>Good to see you here.</h2> </div>
)
 Copy code 

One more thing to note ,JSX The outermost layer of must have one and only one element , Otherwise, an error will be reported , You can also use it <React.Fragment></React.Fragment> perhaps <></> To make your outermost element empty

const element = (
  <> <h1>Hello!</h1> <h2>Good to see you here.</h2> </>
)
 Copy code 

Last , Use ReactDOM.render() Render element to root DOM In nodes

const element = <h1>Hello, world</h1>
ReactDOM.render(element, document.getElementById("root"))
 Copy code 

Conditions apply colours to a drawing

React Condition rendering and JavaScript The same as in China , Use JavaScript Operator if perhaps Conditional operator To create elements to represent the current state , And then let React Update... Based on them UI.

function UserGreeting(props) {
  return <h1>Welcome back!</h1>
}

function GuestGreeting(props) {
  return <h1>Please sign up.</h1>
}

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn
  if (isLoggedIn) {
    return <UserGreeting />
  }
  return <GuestGreeting />
}
 Copy code 

You can also directly JSX Conditional operator in

For example, use the ternary operator condition ? true : false

function GuestGreeting(props) {
  return <div>{props.isLoggedIn ? <UserGreeting /> : <GuestGreeting />}</div>
}
 Copy code 

Or use &&

function Mailbox(props) {
  const unreadMessages = props.unreadMessages
  return (
    <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2>You have {unreadMessages.length} unread messages.</h2>} </div>
  )
}
 Copy code 

list & Key

A common scenario is that we need multiple duplicate components , Render data arrays by traversing them

below , We use Javascript Medium map() Method to traverse numbers Array . Turn each element in the array into <li> label , Finally, we assign the resulting array to listItems

const numbers = [1, 2, 3, 4, 5]
const listItems = numbers.map(number => <li>{number}</li>)
 Copy code 

When we run this code , You will see a warning a key should be provided for list items, It means when you create an element , Must include a special key attribute .

Let's assign one to each list element key Property to resolve the warning above :

const numbers = [1, 2, 3, 4, 5]
const listItems = numbers.map(number => <li key={number.toString()}>{number}</li>)
 Copy code 

key

In the above, we add a unique... To each list item according to the error information key, So this key What is it for ?

key help React Identify which elements have changed , Such as being added or deleted , Pay attention to this key Not for developers , But to react For your own use , You can't be in the component props Get in the key

Of an element key It's best that this element has a unique string in the list . Usually , We use... In the data id As an element key:

const todoItems = todos.map(todo => <li key={todo.id}>{todo.text}</li>)
 Copy code 

When the element is not certain id When , As a last resort, you can use the element index index As key:

const todoItems = todos.map((todo, index) => (
  // Only do this if items have no stable IDs
  <li key={index}>{todo.text}</li>
))
 Copy code 

If the order of the list items may change , It is not recommended to use an index as an index key value , Because this will lead to poor performance , It can also cause problems with component state .

One : use index As key The problems brought about by

In the example above , After adding elements to the array ,key The corresponding instances are not destroyed , It's a renewal . The specific update process we take key=0 To illustrate ,

  • Component re render Get the new virtual dom
  • New and old virtual dom Conduct diff, There are both new and old versions key=0 The components of ,react Think of the same component , Only components can be updated
  • And then compare them children, Find that the text content of the content is different , and input The components don't change , This triggers the componentWillReceiveProps Method , To update the text content of its subcomponents
  • Because of the children in input The components don't change , It is also related to the data passed in by the parent component props There's no connection , therefore input Components don't update ( Namely its componentWillReceiveProps Method will not be executed ), Causes the value entered by the user not to change

Components

Components , Conceptually similar to JavaScript function . It accepts arbitrary input parameters ( namely “props”), And go back to the React Elements .

When do I need components ?

When we reuse a paragraph with a very similar structure JSX When , We should think of this paragraph JSX Pull apart into a separate component

The easiest way to define a component is to write JavaScript function :

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>
}
 Copy code 

Or you can write a component as a separate jsx file , Then introduce it in other files

Component naming must begin with uppercase , Otherwise it will be identified as html label , if html If there is no corresponding element with the same name, an error will be reported

When using components , Similar to a html Use it like a label

<div>
  <Welcome name='Sara' />
  <Welcome name='Cahal' />
  <Welcome name='Edite' />
</div>
 Copy code 

When React When the element is a user-defined component , It will be JSX Received properties (attributes) And sub components (children) Conversion to a single object passed to the component , This object is called “props”.

props There's a special prop children, That is, the child element of the package of the component

function FancyBorder(props) {
  return <div className={"FancyBorder FancyBorder-" + props.color}>{props.children}</div>
}

function WelcomeDialog() {
  return (
    <FancyBorder color='blue'> <h1 className='Dialog-title'>Welcome</h1> <p className='Dialog-message'>Thank you for visiting our spacecraft!</p> </FancyBorder>
  )
}
 Copy code 

It should be noted that props yes read-only Of , in other words , Component cannot modify its own props, Such a function is called “ Pure function ”, Because this function does not attempt to change the input parameter , And the same input parameter always returns the same result under multiple calls , There are no side effects .

in other words , We want to change props Can only be passed in props Time to change

Component communication

Use props , In fact, it completes the communication and value transfer between parent component and child component , But we want to change props When , If you change directly props Value , You will find that the page has not been re rendered , What's going on here ?

I have to introduce it React One provided by hook,useState

We use useState To write a counter component

import { useState } from "react"

function IncreaseButton() {
  const [count, setCount] = useState(0)
  return (
    <> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> </>
  )
}

export default IncreaseButton
 Copy code 

In this component , First, I call useState,useState Will return a pair of values : At present State and a function that lets you update it , You can call this function in the event handler or somewhere else . When using this function to update state after , Use this state Of UI Will re render

useState The only parameter is the initial state. In the example above , Our counter starts from zero , So initially state Namely 0.

Father's son

I understand useState after , We'll know how to communicate between father and son

The parent component passes the parameters to be passed key={xxx} Mode passed to sub components , Subcomponent pass this.props.key To obtain parameters

If you need to change the of the incoming subcomponent props, You only need to use the variables passed to the child components useState The parcel , Then you need to change props When , Use... In the parent component useState Updates provided state Function update of state, The subcomponents will be re rendered

Son father

Father to son and son to father are very similar , It's also the use of useState, One way is to , take useState Simultaneous interpreting updates are provided to sub components. , Next, you can use this function in the child component to update the parent component state 了 .

Brother component communication

Communication between brothers is easier to think of , We can completely promote the state required between brother components to the parent component , Let parent component manage state, In this way, brother component communication can also be completed

Last , We can also use some state management tools to communicate between components , Common are Context HookReduxdva

mobx wait

Event handling

  • React The event was named after a small hump (camelCase), Not just lowercase .
  • Use JSX Syntax, you need to pass in a function as an event handler , Instead of a string .

for example , Conventional HTML:

<button onclick="activateLasers()">Activate Lasers</button>
 Copy code 

stay React Slightly different in :

<button onClick={activateLasers}> Activate Lasers </button>
//  Note that if your function needs to pass in parameters , It needs to be written like this 
<button onClick={() => activateLasers(params)}> Activate Lasers </button>
// because {} The expression inside will execute immediately 
 Copy code 

stay React Another difference is that you can't return by false To prevent default behavior . You must use it explicitly preventDefault. for example , Conventional HTML Block the default submission behavior of forms in , You could write it like this :

<form onsubmit="console.log('You clicked submit.'); return false">
  <button type="submit">Submit</button>
</form>
 Copy code 

stay React in , It might look something like this :

function Form() {
  function handleSubmit(e) {
    e.preventDefault()
    console.log("You clicked submit.")
  }

  return (
    <form onSubmit={handleSubmit}> <button type='submit'>Submit</button> </form>
  )
}
 Copy code 

Controlled components

stay HTML in , Form Elements ( Such as <input>( It is an uncontrolled component )、 <textarea> and <select>) Usually maintain it yourself state, And update according to the user's input . And in the React in , Variable state (mutable state) Usually stored in the state Properties of the , And only by using setState() To update .

We can combine the two , send React Of state Become “ Unique data source ”. Render the form's React Components also control what happens to the form during user input . By React The form input element that controls the value in this way is called “ Controlled components ”.

Setting up controlled components requires 3 A step :

  1. Definition preservation input The state of the value :const [value, setValue] = useState(").
  2. Create an event handler , The event handler updates the state when the value changes :
const onChange = event => setValue(event.target.value);
 Copy code 
  1. by input Field assignment status value , And add event handlers :<input type="text" value={value} onChange={onChange} />.

Here is a simple example

import { useState } from "react"

const MyInput = () => {
  const [value, setValue] = useState("")
  return (
    <> <input value={value} type='text' onChange={e => setValue(e.target.value)} /> <p>{value}</p> </>
  )
}

export default MyInput
 Copy code 

High order component HOC

Higher order components are components with parameters , Function whose return value is the new component

for example redux Inside connect

function mapStateToProps(state) {
  return { todos: state.todos }
}

export default connect(mapStateToProps)(TodoApp)
 Copy code 

Hook

When introducing components , We have learned useState This hook, Next , Let's learn more about hook

Hook Are some that allow you to be in a function component “ Hook in ” React state And life cycle .Hook Can't be in class Used in components —— This makes you not use class Can also be used React.

️ There are two other points to note

  • Can only be called on the outermost layer inside a function Hook, Don't cycle 、 Call in conditional judgement or subfunction
  • Can only be invoked in functional components. Hook, Don't be in the other JavaScript Call in function

Reasons for restriction

The second point is easy to think of , But the first point is why ?

This is to ensure our Hooks Called in the same order in each render . This makes React Able to useState and useEffect Keep between calls hook Correct status .

With Preact Of Hook For example , It USES Arrays and Subscripts To achieve Hook Lookup (React Use the list , But the principle is similar )

//  At present  hook  Global index of 
let currentIndex

//  Currently running components 
let currentComponent

//  First call  currentIndex  by  0
useState('first') 

//  Second call  currentIndex  by  1
useState('second')
 Copy code 

It can be seen that , Every time Hook All calls correspond to a global index Indexes , Use this index to the currently running component currentComponent Upper _hooks Find saved values in array , That is to say Hook Back to [state, useState]

So if the condition is called , Like the first one useState Only 0.5 The probability of being called :

//  At present  hook  Global index of 
let currentIndex

//  Currently running components 
let currentComponent

//  First call  currentIndex  by  0
if (Math.random() > 0.5) {
  useState('first')
}

//  Second call  currentIndex  by  1
useState('second')
 Copy code 

stay Preact When rendering components for the first time , hypothesis Math.random() The random value returned is 0.6, So the first one Hook Will be performed , The saved on the component _hooks Status is :

_hooks: [
  { value: 'first', update: function }, { value: 'second', update: function }, ]  Copy code 

The search process is represented by a graph :

Suppose the second rendering ,Math.random() The random value returned is 0.3, There is only the second one useState Carried out , Then its corresponding global currentIndex Would be 0, Go to at this time _hooks[0] What I got in the book was first The corresponding state , This will cause rendering confusion .

you 're right , Should have been second Of value, Inexplicably pointed first, Rendering is completely wrong !

Of course, we can think that since we look up in array order hook It could be wrong , Then we'll give everyone hook Specify a unique key Well ? in fact React The team also considered adding one for each call key Value design , But multiple flaws led to the rejection of the proposal , stay Why are sequential call pairs React Hooks Very important ? You can learn more about the reasons in .

useState

const [state, setState] = useState(initialState);
 Copy code 

Each rendering is a separate closure

  • Every rendering has its own Props and State
  • Each rendering has its own event handler
  • When you click update status , Function components will be called again , Then each rendering is independent , The value obtained will not be affected by subsequent operations
function Counter2() {
  let [number, setNumber] = useState(0)
  function alertNumber() {
    setTimeout(() => {
      // alert  Only the state when the button is clicked can be obtained 
      alert(number)
    }, 3000)
  }
  return (
    <> <p>{number}</p> <button onClick={() => setNumber(number + 1)}>+</button> <button onClick={alertNumber}>alertNumber</button> </>
  )
}
 Copy code 

Functional update

If the new state Need to use the previous state calculated , Then you can pass the callback function as a parameter to setState. The callback function will receive the previous state, And return an updated value

function Counter() {
  let [number, setNumber] = useState(0)
  function lazy() {
    setTimeout(() => {
      // setNumber(number+1);
      //  In this way, it will be obtained every time it is executed  state, Instead of using the click trigger  state
      setNumber(number => number + 1)
    }, 3000)
  }
  return (
    <> <p>{number}</p> <button onClick={() => setNumber(number + 1)}>+</button> <button onClick={lazy}>lazy</button> </>
  )
}
 Copy code 

Initial value of inertia

useState The initial value of is inert , Works only when the component is first rendered . If state The initial value of needs to be obtained through complex calculation ,useState The initial value of can also be a function , The return value of the function will be useState The initial value of the .

function Counter5(props) {
  console.log("Counter5 render")
  //  This function is executed only once during the initial rendering , When a component is re rendered with subsequent update status , This function will no longer be called 
  function getInitState() {
    return { number: props.number }
  }
  let [counter, setCounter] = useState(getInitState)
  return (
    <> <p>{counter.number}</p> <button onClick={() => setCounter({ number: counter.number + 1 })}>+</button> <button onClick={() => setCounter(counter)}>setCounter</button> </>
  )
}
 Copy code 

Do not pass in the same state

In the use of useState Of dispatchAction to update state When , Remember not to pass in the same state, This will keep the view from updating . For example, it's written below :

export default function Index(){
    const [ state  , dispatchState ] = useState({ name:'alien' })
    const  handleClick = ()=>{ //  Click button , View not updated .
        state.name = 'Alien'
        dispatchState(state) //  Direct change  `state`, The address pointed to in memory is the same .
    }
    return <div> <span> { state.name }</span> <button onClick={ handleClick } >changeName++</button> </div>
}
 Copy code 

In the example above , When the button is clicked , The view was found unchanged , Why is this cause ?

stay useState Of dispatchAction In processing logic , It will be shallow twice state , Find out state identical , The update scheduling task will not be started ; demo Twice state Points to the same memory space , So the default is state equal , No view updates will occur .

resolvent : Put the above dispatchState Change to dispatchState({...state}) The problem has been fundamentally solved , Shallow copy object , Re requested a memory space .

setState Is it synchronous or asynchronous ?

About state There is another classic question , That's it setState yes “ Sync ” or “ asynchronous ” Of ?

We know Promise.then(),setTimeout It's asynchronous execution . from js In terms of execution , setState It must be synchronous execution .

So synchronous and asynchronous are not discussed here setState Whether to execute asynchronously , It's a call setState after this.state Can I update it now .

constructor(props) {
    super(props);
    this.state = {
      data: 'data'
    }
  }

  componentDidMount() {
    this.setState({
      data: 'did mount state'
    })

    console.log("did mount state ", this.state.data);
    // did mount state data

    setTimeout(() => {
      this.setState({
        data: 'setTimeout'
      })

      console.log("setTimeout ", this.state.data);
    })
  }
 Copy code 

And the output of this code , first console.log Will be output data , And the second one. console.log Will be output setTimeout . For the first time setState When , It's asynchronous , The second time setState When , It becomes synchronous again .

In the end setState What is asynchronous and what is synchronous ? Why? ?

Say first conclusion

As long as you get in react Scheduling process , That's asynchronous . As long as you don't get in react Scheduling process , That's synchronous .

by React Event handler for control , And lifecycle function calls setState Updates will not be synchronized state . React Calls in events beyond control setState It's synchronized . Like native events ,setTimeout/setInterval etc. .

and setState In the case of synchronous execution , DOM Will also be updated synchronously , Which means that if you do it many times setState , Will result in multiple updates , It's meaningless and a waste of performance . In the case of asynchronous execution, multiple state Will be combined for batch update

domo

But if you try on your own FC Of setTimeout To call setState after , Print state , You'll find that he hasn't changed , Then you will be very confused , Why? ? Isn't this synchronous ?

This is because of a closure problem , You got the last one state , The printed value is naturally the last one , This is the real time state Has been changed .

From the perspective of source code ,setState Our behavior is “ asynchronous ” still “ Sync ” Depending on React perform setState Methods Execution context (ExecutionContext).

If ExecutionContext by NoContext (0), Indicates that there are no other tasks currently in progress , be setState yes “ Sync ” Of .

React Source code address :

 if (
      (executionContext & LegacyUnbatchedContext) !== NoContext &&
      (executionContext & (RenderContext | CommitContext)) === NoContext
    ) {
       // ....  Omit some code that will not be covered in this discussion 
    } else {
      ensureRootIsScheduled(root, eventTime); //  Trigger scheduler Dispatch ( Scheduling is asynchronous ) ,  Therefore, the function will not be triggered immediately render.
      if (executionContext === NoContext) {  //  When the execution context is 0 when ,  The synchronization queue is refreshed 
         // ....  Omit some code that will not be covered in this discussion 

        //  Here's the key ,  Execute synchronous callback queue .  Interested students can continue to view in the source code ,  It can be concluded that :
        // if Outside the branch ensureRootIsScheduled(root, eventTime) And here flushSyncCallbackQueue()
        //  In the end, they call performSyncWorkOnRoot Conduct fiber Cyclic construction of tree 
        flushSyncCallbackQueue(); 
      }
    }

 Copy code 

Notice the Execution context Not browser , In order to better control the rendering task , Avoid occupying the browser's main thread for a long time , React Achieve your own Execution context .

Just bypass react Internal trigger change executionContext The logic of , Can guarantee executionContext It's empty , To realize setState For synchronization .

The other thing to note is that

The above analysis is based on legacy Pattern analysis , as everyone knows react the ( Probably ) To enter in full concurrent Pattern ( You can refer to react Boot mode ). stay concurrent In mode , This topic may be meaningless , Because from the latest code , stay concurrent There is no judgment at all in mode executionContext, therefore concurrent In mode setState Are asynchronous

The first 18 topic :React in setState When is synchronization , When is asynchronous ? This issues The following is a detailed discussion

useEffect

useEffect(didUpdate);
 Copy code 

useEffect You can let us execute in a function component side effect operation . Event binding , Data request , Dynamic modification DOM.

useEffect Will be in every time React Execute... After rendering . Whether it's the first time you mount it , Or update .( Of course, we can control this behavior )

useEffect Hook Can be seen as componentDidMount,componentDidUpdate and componentWillUnmount The combination of these three life cycle functions .

That need not be removed effect

Such as sending network requests , Manual change DOM, Log , These are common operations that do not need to be cleared . Because after we do this , You can ignore them .

A simple example

import { useState, useEffect } from "react"

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

  useEffect(() => {
    document.title = `You clicked ${count} times`
  })

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

What needs to be removed effect

Like listening to events , Timers, etc

effect You can return a function , When react When clearing , Will execute the returned function . Every time I execute this effect when , They'll be right up to the last one effect Scavenging . It also performs cleanup when the component is uninstalled .

in other words , In the following code . Every update , Will be right about the last time effect To uninstall , And carry out this effect.

import { useEffect, useState } from "react"

function App() {
  const [count, setCount] = useState(0)
  useEffect(() => {
    function handleClick() {
      console.log("click body")
    }
    console.log(" Add event ")
    document.body.addEventListener("click", handleClick)
    return () => {
      console.log(" Uninstall Events ")
      document.body.removeEventListener("click", handleClick)
    }
  })

  return (
    <div className='App'> <h1>{count}</h1> <button onClick={() => setCount(count + 1)}> to update </button> </div>
  )
}

export default App
 Copy code 

effect performance optimization

Every time you execute effect, Clear the last effect Unnecessary performance waste may result . We can go through effect Second parameter of , control effect Implementation . The second parameter is useEffect Dependence , Only when dependencies change ,useEffect Will update .

import { useEffect, useState } from "react"

function App() {
  const [count, setCount] = useState(0)
  const [num, setNum] = useState(0)

  useEffect(() => {
    console.log(" Initial rendering and count Execute on update ")
  }, [count])

  return (
    <div className='App'> <h1>count:{count}</h1> <h1>num:{num}</h1> <button onClick={() => setCount(count + 1)}> to update  count</button> <button onClick={() => setNum(num + 1)}> to update  num</button> </div>
  )
}

export default App
 Copy code 

When we pass an empty array as a dependency , Will tell React,effect Do not rely on any state perhaps props. We can use this behavior simulation componentDidMount perhaps componentWillUnmount. The most common application is in componentDidMount Phase is used to send requests

import { useEffect, useState } from "react"

function App() {
  const [count, setCount] = useState(0)
  const [num, setNum] = useState(0)

  useEffect(() => {
    console.log(" Called only when the component is mounted ")
  }, [])

  return (
    <div className='App'> <h1>count:{count}</h1> <h1>num:{num}</h1> <button onClick={() => setCount(count + 1)}> to update  count</button> <button onClick={() => setNum(num + 1)}> to update  num</button> </div>
  )
}

export default App
 Copy code 

How to be in useEffect Use in async/await?

async Function will return a by default Promise object , and useEffect in , Only return nothing or return a cleanup function . Console , The following warnings are triggered Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect..

The solution is as follows :

useEffect(() => {
  const fetchData = async () => {
    const response = await fetch("https://api.example.com/")
    const data = await response.json()
    setCount(data.length)
  }
  fetchData()
}, [])
 Copy code 

useContext

const value = useContext(MyContext);
 Copy code 

Receive one context object (React.createContext The return value of ), And return to the current context Of value value .useContext You can subscribe to context The change of . However, it still needs to be used by upper components <MyContext.Provider> To provide the underlying components with context.

import { createContext, useContext, useState } from "react"

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee",
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222",
  },
}

const ThemeContext = createContext(themes.light)

function App() {
  const [theme, setTheme] = useState(themes.dark)
  return (
    <> <ThemeContext.Provider value={theme}> <ThemedButton /> </ThemeContext.Provider> <ThemedButton /> <button onClick={() => { setTheme(themes.light) console.log("click") }} > change theme </button> </>
  )
}

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

export default App
 Copy code 

useReducer

useReducer Receive three parameters , Form like (state, action) => newState Of reducer function ,initialArg Initial value ,init Inert initial value function . If three parameters are passed in ,init(initialArg) Use as initial value .

const [state, dispatch] = useReducer(reducer, initialArg, init)
 Copy code 

useReducer and useState Works in a similar way , but useReducer In complex scenes useState More suitable . for example state The logic is complex and contains multiple subvalues , Or next state Depend on the previous state etc. . also , Use useReducer It can also optimize the performance of components that trigger deep updates , Because you can pass dispatch Instead of callback functions

Here are the USES reducer Counter example for :

const initialState = { count: 0 }

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 }
    case "decrement":
      return { count: state.count - 1 }
    default:
      throw new Error()
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState)
  return (
    <> Count: {state.count} <button onClick={() => dispatch({ type: "decrement" })}>-</button> <button onClick={() => dispatch({ type: "increment" })}>+</button> </>
  )
}
 Copy code 

useCallback

useCallback Receive callback function and dependency array as parameters .useCallback Returns the memoized function . When dependencies change , Will return a new memoized function .

const memoizedCallback = useCallback(() => {
  doSomething(a, b)
}, [a, b])
 Copy code 

useMemo

useMemo and useCallback similar .useMemo Returns the memoized value . When dependencies change , Will recalculate memoized value .

Feel similar vue Calculation properties in

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
 Copy code 

useCallback(fn, deps) amount to useMemo(() => fn, deps).

useRef

const refContainer = useRef(initialValue)
 Copy code 

useRef Return to a variable ref object , Its .current Property is initialized to the parameter passed in (initialValue). Back to ref Objects remain the same throughout the life cycle of the component .

A common use case is imperative access to subcomponents :

function TextInputWithFocusButton() {
  const inputEl = useRef(null)
  const onButtonClick = () => {
    // `current`  The point has been mounted to  DOM  Text input elements on 
    inputEl.current.focus()
  }
  return (
    <> <input ref={inputEl} type='text' /> <button onClick={onButtonClick}>Focus the input</button> </>
  )
}
 Copy code 

useRef In addition to getting dom Outside the function of the node ,useRef Of current attribute , It is convenient to save any variable value .useRef Every time you render , Will return the same ref object .

We can use useRef To save the timer timer, So that when the component is destroyed , Destroy timer correctly .

const timerRef = useRef()
const [count, setCount] = useState(0)
useEffect(() => {
  timerRef.current = setInterval(() => {
    setCount(pre => pre + 1)
  }, 1000)
  return () => {
    timerRef.current && clearInterval(timerRef.current)
  }
}, [])
 Copy code 

️ Customize Hook

  1. Customize Hook Must be use start
  2. Customize Hook Just logical reuse , Among them state It won't be shared .
  3. Customize Hook Other functions can be called internally Hook.
  4. Avoid premature splitting of abstract logic

Custom data acquisition Hook

We go through ajax When requesting cousin data , A lot of logic is universal . such as loading Processing of status , Processing of error messages , Page turning processing . We can abstract these logic into a public Hook. Different api, As custom Hook Parameters of .

Here is a data request customization Hook Example :

import { useEffect, useState } from "react"

function useDataApi(api) {
  //  the number of pages 
  const [page, setPage] = useState(1)
  // loading state 
  const [loading, setLoading] = useState(false)
  //  error message 
  const [error, setError] = useState("")
  //  data 
  const [data, setData] = useState([])
  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true)
        setError("")
        const result = await getData(api + page)
        setData(result.data)
      } catch (e) {
        setError(e)
      } finally {
        setLoading(false)
      }
    }
    fetchData()
  }, [page])
  return { loading, error, data, page, setPage }
}

export default useDataApi
 Copy code 

Life cycle

First, it shows that functional components do not have a real life cycle , Because the essence of function components is functions , No, state The concept of , Therefore, there is no life cycle theory , Just one render Function only .

But we can use useStateuseEffect() and useLayoutEffect() To simulate the implementation lifecycle .

We can class Component declares some special methods , These methods are executed when the component is mounted or unloaded , These methods are called Life cycle approach

Each component contains “ Life cycle approach ”, You can override these methods , To facilitate the execution of these methods at specific stages of the run

class Life cycle of components

We can divide the life cycle into three stages :

  • Mount stage
  • Component update phase
  • Unloading phase
  1. mount

    When a component instance is created and inserted DOM In the middle of the day , Its life cycle call sequence is as follows :

    • constructor(): Constructors ( initialization ), Be careful not to props Copy the value of to state
    • static getDerivedStateFromProps(): from props Get derived from state, Used in place of componentWillMount
    • render(): Component mounting ,react The most important step , Create virtual dom, Conduct diff Algorithm , to update dom The trees are all here
    • componentDidMount(): The component is mounted
  2. to update

    When the components props or state Updates are triggered when changes occur . The order of component update lifecycle calls is as follows :

    • static getDerivedStateFromProps(): coordination componentDidUpdate, You can override componentWillReceiveProps All uses of
    • shouldComponentUpdate(): Determine whether to update the status
    • render(): Component mounting
    • getSnapshotBeforeUpdate: obtain DOM Before updating Snapshot
    • componentDidUpdate(): Update complete
  3. uninstall

    • componentWillUnmount: Before component uninstall

    You can go to ** Life cycle map ** Detailed view

constructor

stay mount Stage , The first function to execute , Used to instantiate React Components , If not initialized state Or no method binding , It doesn't need to be React Component implementation constructor .

constructor(props) {
  super(props);
  //  Don't call here  this.setState()
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}
 Copy code 

getDerivedStateFromProps

trigger :state change 、props change 、forceUpdate, Pictured above .

This is a static method , Is a and component itself " Unrelated " Role . In this static method , Except for two default location parameters nextProps and currentState outside , you cannot access Data on any component .

//  initialization / Call when updating 
static getDerivedStateFromProps(nextProps, currentState) {
  console.log(nextProps, currentState, "getDerivedStateFromProps Method execution ");
  //  The return value is for currentState A merger 
  return {
    fatherText: nextProps.text,
  };
}
 Copy code 

render

render() The method is class The only method that must be implemented in a component , return JSX

Be careful : If shouldComponentUpdate() return false, Will not call render()

componentDidMount

It is mainly used to do some operations when the component is loaded , For example, launching network requests or binding events . treat as vue Of mounted Just use it , What needs to be noted here is :

componentDidMount() Directly call setState(). It will trigger additional rendering , That's twice render, But it's not a big problem , The main thing is understanding .

shouldComponentUpdate

This method returns true perhaps false To determine if a new rendering needs to be triggered . Because rendering triggers the last level , So too. performance optimization Where we have to fight . By adding judgment conditions to prevent unnecessary rendering . Be careful : First render Or use forceUpdate() This method will not be called when .

React The government has provided a general optimization scheme , That is to say PureComponent.PureComponent The core principle is that it is implemented by default shouldComponentUpdate function , In this function, I'm going to props and state Conduct Shallow comparison , Used to determine whether an update is triggered .

Of course PureComponent There is a shortcoming Of , When you use it, you must pay attention to : Because of the shallow comparison , May be due to the deep data inconsistency caused by the wrong negative judgment , This results in the page No updates available . Not suitable for use in the presence of Multiple nested objects Of state and prop in .

shouldComponentUpdate(nextProps, nextState) {
  //  Shallow comparison only compares values and references , It's not true  Object  Compare the values of each item in 
  if (shadowEqual(nextProps, this.props) || shadowEqual(nextState, this.state) ) {
    return true
  }
  return false
}
 Copy code 

getSnapshotBeforeUpdate

stay DOM Before updating Called , The return value will be taken as componentDidUpdate() The third parameter of , Otherwise undefined

componentDidUpdate

componentDidUpdate() Will be called immediately after the update . The first rendering does not do this .

When the component is updated , You can do it here DOM To operate . If you are on the update before and after props Made a comparison , You can also choose to make a network request here .( for example , When props When there is no change , The network request will not be executed ).

componentDidUpdate(prevProps) {
  //  characteristic use ( Don't forget to compare  props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}
 Copy code 

You can also be in componentDidUpdate() in Call directly setState(), But please pay attention to It has to be wrapped in a conditional statement , Deal with it as in the example above , Otherwise, it will lead to a dead cycle .

componentWillUnmount

componentWillUnmount() It will be called directly before the component is unloaded and destroyed . Perform the necessary cleanup operations in this method , for example , eliminate timer, Cancel network request or clear in componentDidMount() Subscriptions created in, etc .

Error handling

There are also two life cycles for error handling

  • static getDerivedStateFromError()
  • componentDidCatch()

Simulating life cycles in functional components

  • constructor: Function components do not need constructors , We can do it by calling useState To initialize the state. If the cost of calculation is more expensive , You can also pass a function to useState.

    const [num, UpdateNum] = useState(0)
     Copy code 
  • getDerivedStateFromProps: In general , We don't need to use it , We can do it in Update during rendering state, To achieve getDerivedStateFromProps Purpose .

    function ScrollView({row}) {
      let [isScrollingDown, setIsScrollingDown] = useState(false);
      let [prevRow, setPrevRow] = useState(null);
    
      if (row !== prevRow) {
        // Row  Has changed since the last rendering . to update  isScrollingDown.
        setIsScrollingDown(prevRow !== null && row > prevRow);
        setPrevRow(row);
      }
    
      return `Scrolling down: ${isScrollingDown}`;
    }
     Copy code 

    React Will immediately exit the first rendering and use the updated state Rerun components to avoid consuming too much performance .

  • shouldComponentUpdate: It can be used React.memo Wrap a component to its props Make a shallow comparison

    const Button = React.memo(props => {
      //  Specific components 
    })
     Copy code 

    Be careful :React.memo Equivalent to PureComponent, It's only shallow props. It can also be used here useMemo Optimize every node .

  • render: This is the function component body itself .

  • componentDidMount, componentDidUpdateuseLayoutEffect It's the same call phase as both of them . however , I recommend you use it first useEffect, Only try to use it when it goes wrong useLayoutEffect.useEffect Can express all these combinations .

    // componentDidMount
    useEffect(() => {
      //  Need to be in  componentDidMount  What to do 
    }, [])
    
    useEffect(() => {
      //  stay  componentDidMount, as well as  count  When changes  componentDidUpdate  What to do 
      document.title = `You clicked ${count} times`
      return () => {
        //  Need to be in  count  When changes  componentDidUpdate( Precede  document.title = ...  perform , Comply with clean before update )
        //  as well as  componentWillUnmount  What to do 
      } //  When the function of  Cleanup  Functions are executed in the order defined in the code , It has nothing to do with the properties of the function itself 
    }, [count]) //  Only in  count  Update on change 
     Copy code 
  • componentWillUnmount: amount to useEffect Back inside cleanup function

    // componentDidMount/componentWillUnmount
    useEffect(() => {
      //  Need to be in  componentDidMount  What to do 
      return function cleanup() {
        //  Need to be in  componentWillUnmount  What to do 
      }
    }, [])
     Copy code 
  • componentDidCatch and getDerivedStateFromError: at present Not yet These methods Hook Equivalent writing , But soon it will add .

For the convenience of memory , It is summarized in the following table .

class Components Hooks Components
constructor useState
getDerivedStateFromProps useState Back to update function
shouldComponentUpdate useMemo
render The function itself
componentDidMount useEffect
componentDidUpdate useEffect
componentWillUnmount useEffect The function returned in
componentDidCatch nothing
getDerivedStateFromError nothing

The execution order of multiple components

Father son component

  • Mount stage

    branch Two Stage :

    • The first One Stage , From the parent component to its own render, Analyze which sub components need to be rendered , And to them Sub components of synchronization Create , Press Recursion sequence Execute sub components one by one to render, Generated to the parent-child component Virtual DOM Trees , and commit To DOM.
    • The first Two Stage , here DOM The node has been generated , Component mount complete , Start the follow-up process . First, trigger the synchronization sub components in turn componentDidMount, Finally trigger the .

    Be careful : If the parent component contains a different child component , It will be created after the parent component is mounted .

    So the order of execution is :

    Parent component getDerivedStateFromProps —> Synchronize subcomponents getDerivedStateFromProps —> Synchronize subcomponents componentDidMount —> Parent component componentDidMount —> Step sub assembly getDerivedStateFromProps —> Step sub assembly componentDidMount

  • Update phase

    React The design follows a one-way data flow model , in other words , Data flows from the parent component to the child component .

    • The first One Stage , Start with the parent component , perform

      1. static getDerivedStateFromProps
      2. shouldComponentUpdate

      Update to your render, Analyze which sub components need to be rendered , Also on Child components Create , Press Recursion sequence Execute sub components one by one to render, Generated to the parent-child component Virtual DOM Trees , And with the existing Virtual DOM Trees Compare , To calculate the Virtual DOM The real part of change , And only for this part of the original DOM operation .

    • The first Two Stage , here DOM The node has been generated , Component mount complete , Start the follow-up process . First, trigger the following functions of the synchronization subcomponent , Finally trigger the .

      1. getSnapshotBeforeUpdate()
      2. componentDidUpdate()

      React These functions are executed in the order above , Each function is the first execution of each subcomponent , Then there is the execution of the parent component .

      So the order of execution is :

      Parent component getDerivedStateFromProps —> Parent component shouldComponentUpdate —> Child components getDerivedStateFromProps —> Child components shouldComponentUpdate —> Child components getSnapshotBeforeUpdate —> Parent component getSnapshotBeforeUpdate —> Child components componentDidUpdate —> Parent component componentDidUpdate

  • Unloading phase

    componentWillUnmount(), In sequence The first execution of the parent component , The subcomponents are as follows JSX The order defined in executes the respective methods in turn .

    Be careful : If you uninstall an old component with the creation of a new component , The new component will be created and executed first render, Then uninstall the old components you don't need , Finally, the new component performs the callback after the mount .

Brother components

  • Mount stage

    If it's a synchronous route , The order in which they are created and defined in the common parent component is Agreement Of .

    If it's an asynchronous route , The order in which they were created and js Loading is done in the same order .

  • Update phase 、 Unloading phase

    The communication between sibling nodes is mainly through the parent component (Redux and Context Also passed down by changing the parent component props Realized ), Satisfy React The design follows a one-way data flow model , So communication between any two components , In essence, it can be attributed to the update of parent-child components .

    therefore , Brother component update 、 Unloading phase , Please refer to Father son component .

route

When we write a project , There may not be only one page , At this time, you need to introduce routing , The following describes the commonly used react-router

Install dependencies first

npm install react-router-dom
yarn add react-router-dom
 Copy code 

Routing jump

Route jumps can be used Link Or is it NavLink Components

import {Link, NavLink} from "react-router";
<Link activeClassName="nav-active" className="nav" to="/about">About</Link>
<NavLink activeClassName="nav-active" className="nav" to="/home">Home</NavLink>
 Copy code 
  • activeClassName: On the current route , The corresponding component will automatically add the class
  • className: Current component class name
  • to: The route corresponding to the current component

Link Components and NavLink All components can perform route jump , The difference lies in : Corresponding to the current route NavLink It will automatically add class: active, and Link Can't .

You can also use useHistory

import { useHistory } from "react-router-dom";

function HomeButton() {
  let history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}> Go home </button>
  );
}
 Copy code 

Registered routing

import {Route} from "react-router";
<Route path="/home" component={Home}></Route>
<Route exact path="/about" component={About}></Route>
 Copy code 
  • path: The route to be monitored
  • component: The component that the route is bound to ( Or put the components directly in the Route Child elements )
  • exact: Optional , When not written false, Whether to choose strict matching

When the current route corresponds to the route bound by the routing component , The bound component is displayed .

Routing strict matching and fuzzy matching

Routing is more than just one level , Sometimes there are multiple levels of nesting , such as :

http://localhost:3000/home/a/b/c
 Copy code 

Fuzzy matching : If the current route is equal to or contains ( Attention hierarchy ) The situation of , Then enable the component

  • http://localhost:3000/home/a/b/c Contains the route /home
  • http://localhost:3000/a/b/home/c No route is included /home( The hierarchy is wrong )

Strictly match : If the current route is equal to the matching route , To enable the component

  • http://localhost:3000/home And routing /home equal
  • http://localhost:3000/home/a And routing /home It's not equal

Redirection Route

When the user enters a route without , After that, we need to redirect the page to an existing page

At this time, we need <Redirect />

import {Redirect, Route} from "react-router";
<Route ....../>
<Route ....../>
<Redirect to="/home"/>
 Copy code 
  • to: Which route to redirect to

Redirect Need to be placed in all Route below , When the above Route When it doesn't match , Then the route will be redirected to the specified route .

Switch route

Use Switch The components wrap all the Route and Redirect, When multiple matching routes appear , Only the first matching component is rendered .

import {Switch, Route, Redirect} from "react-router";
<Switch> <Route ..../> <Route ..../> <Redirect to="..."/> </Switch>
 Copy code 

Router

You want to use the routing jump component and the routing component , There's one router component left , At the same time, the router component must contain these two components .

Generally in order to make the whole React application You can use routing components , So we usually wrap the router on the root component .

import {HashRouter, BrowserRouter} from "react-router";
ReactDOM.render( 
    <HashRouter> <App/> </HashRouter>,
    document.querySelector("#root")
);
 Copy code 

There are two kinds of router components , Namely HashRouter And BrowserRouter( namely vue Medium history Pattern ), They correspond to two routing methods respectively .

The difference between the two HashRouter Mode of url Finally, there is one # Number , and BrowserRouter No,

Usually use HashRouter, Because it's the simplest , Server side rendering is not required

Parameter passing in routing

params

<Route path="/home/message/detail/:id" component={Child} />
 Copy code 

:id representative id Is a variable parameter

Subcomponent pass useParams obtain

const { id } = useParams();
 Copy code 

search

<Route path="/home/message/detail/?id='xxx'" component={Child} />
 Copy code 

react-router No resolution provided search Parametric api, We need to analyze it ourselves

utilize qs library

const {id} = qs.parse(this.props.location.search);
 Copy code 

State management

Context Hooks

utilize context + reducer The implementation is similar to redux State management , But there are some performance problems that we need to optimize ourselves

redux

Well-known JavaScript State Management Library , also react-redux, Make in react Use in redux Become more convenient

dva

mobx

Reference article

React Official documents

react-router Official documents

Hooks And React Life cycle relationships

「 Learning notes 」ReactHooks introduction

I broke it React Hook Must be in order 、 Shackles that cannot be invoked in conditional statements

copyright notice
author[Jiaxin cansiny],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210827075009249u.html

Random recommended