React Quick Start

React is a JavaScript library for building user interfaces. React effectively updates and renders components correctly when data changes.

When we write React, we create components that have their own states and then form a more complex UI from those components.

The first component:

import React from 'react';

class HelloReact extends React.Component {
  render() {
    return (
      <div>
        Hello {this.props.name}
      </div>); }}export default HelloReact;
Copy the code

The component has the capability to dynamically render the name field passed in where the component is used.

<HelloReact name={"First component"} / >Copy the code

So we can render a DOM structure correctly

It is through this continuous combination of various stateful components that a complex industrial project is constructed.

This article code hosting address >>> please click

JSX

JSX will probably be your first introduction to new concepts, since you’ll probably be writing “HTML” directly in JS files for the first time.

return (
  <div>
  	Hello {this.props.name}
  </div>
);
Copy the code

JSX is a syntax extension for JavaScript that generates React “elements.”

It is written exactly as follows:

import React from 'react';

class HelloJSX extends React.Component {
  render() {
    return React.createElement("div".null."Hello ".this.props.name); }}export default HelloJSX;
Copy the code

CreateElement (“div”, null, “Hello “, this.props. Name) React allows you to create a set of data structures that represent elements in React. Finally, convert it into a real DOM and insert it into the page.

Thus, JSX’s structure is only similar to the real DOM structure, and React is required to perform a series of transformations between them.

What is the nature of JSX?

JSX itself is syntactic sugar in the React project and is not directly recognized by the browser. It has to be compiled, so this compiled thing is essentially what it is.

JSX is compiled in the React project into the React element, which is a tree-like data structure. This is what we call the virtual DOM.

Components by value

Since Reac T is a combination of components to achieve the construction of complex projects. Then the first problem between components is how they communicate with each other.

props

Pass data to subcomponents through props, andAll React components must protect their props from being changed like pure functions.

import React from 'react';

class Child extends React.Component {
  render() {
    const { list } = this.props

    return <ul>{list.map((item, index) => {
      return <li key={item.id}>
        <span>{item.title}</span>
      </li>
    })}</ul>}}class Parent extends React.Component{
  constructor(props) {
    super(props)
    this.state = {
      list: [{id: 'id-1'.title: 'heading 1'
        },
        {
          id: 'id-2'.title: 'title 2'
        },
        {
          id: 'id-3'.title: 'title'}}}]render() {
    return <Child list={this.state.list} />}}export default Parent;
Copy the code

Code explanation:

  1. The Parent component passes its list state to its children<Child list={this.state.list} /> 
  2. The Child component gets the data passed by the parent component via props and renders it successfullyconst { list } = this.props 

This is the parent passing data to the child.

Consider this: what if our component hierarchy is deeply nested, such as a Parent component that needs to pass some data to a Child’s Child or even deeper level? You can do that by context.

Context

Context provides a way to share such values between components without explicitly passing props layer by layer through the component tree.

We often use it to pass application-level configuration data, such as APP themes, user preferences, and so on.

Let’s implement a simple scenario that switches themes:

import React from 'react'

// Create a Context and fill in the default value (any js variable)
const ThemeContext = React.createContext('light') / / {1}

// Functional components can use Consumer
function ThemeLink (props) {
  return <ThemeContext.Consumer> // {3}
    { value => <p>link's theme is {value}</p> }
  </ThemeContext.Consumer>
}

class ThemedButton extends React.Component {
  render() {
    const theme = this.context // React will find the nearest theme Provider and use its value.
    return <div>
      <p>button's theme is {theme}</p>
    </div>
  }
}
ThemedButton.contextType = ThemeContext // Specify contextType to read the current theme context.

// Intermediate components no longer have to specify the theme to pass down.
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
      <ThemeLink />
    </div>)}class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      theme: 'light'}}render() {
    return <ThemeContext.Provider value={this.state.theme}>/ / {2}<Toolbar />
      <hr/>
      <button onClick={this.changeTheme}>change theme</button>
    </ThemeContext.Provider>
  }
  changeTheme = () = > {
    this.setState({
      theme: this.state.theme === 'light' ? 'dark' : 'light'}}})export default App
Copy the code

Code explanation:

  • {1} const ThemeContext = React.createContext('light')Create a context that returns a ThemeContext object
  • {2} <ThemeContext.Provider value={this.state.theme}></ThemeContext.Provider>Provider of ThemeContext
  • {3} <ThemeContext.Consumer></ThemeContext.Consumer>The consumer of ThemeContext
The React. CreateContext source code
export function createContext(defaultValue, calculateChangedBits){
  const context = {
    ?typeof: REACT_CONTEXT_TYPE,
    _calculateChangedBits: calculateChangedBits,
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    Provider:null.Consumer:null}; context.Provider = { ?typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };

  return context;
}
Copy the code

This is the truncated source code. Calling createContext returns a context object, and we use themecontext. Provider, which is one of the properties returned by this object.

state

Now that we know how components communicate with each other, how do components maintain their state? It’s state, because of some of its features that make it a required test for interviews.

import React from 'react'

class StateDemo extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  add = () = >{
    this.setState({
      count: this.state.count + 1})}render() {
    return <div>
      <p>{this.state.count}</p>
      <button onClick={this.add}>increase</button>
    </div>}}export default StateDemo
Copy the code

Code explanation:

  1. Initialize the component’s state object in Constructor
  2. Change the state of state by using setState when the button is clicked

This is the state of the component, and how to change it.

  1. Is setState synchronous or asynchronous?
  2. Why sometimes twice in a rowsetState Only once?
  3. Why must state be immutable

SetState Asynchronous state

1.1 setState in the React lifecycle

componentDidMount() {
  console.log('call SetState SetState);
  this.setState({
    index: this.state.index + 1
  })
  console.log('state'.this.state.index); / / 0

  console.log('call SetState SetState);
  this.setState({
    index: this.state.index + 1
  })
  console.log('state'.this.state.index); / / 0
}
Copy the code

Both prints are 0, not updated immediately

1.2 React Synthesizes the setState in the event

  add = () = >{
    console.log('Call setState in synthetic event');

    this.setState({
      count: this.state.count + 1
    })

    console.log('count'.this.state.count); / / 0

    console.log('Call setState in synthetic event');

    this.setState({
      count: this.state.count + 1
    })

    console.log('count'.this.state.count); / / 0

  }
Copy the code
  • Both times the output is 0 and not immediately updated
  • This is set twice, but the page only increases by 1, indicating that it has been merged

SetState Synchronization status

1.1 Perform setState in asynchronous functions

  componentDidMount() {
    setTimeout(() = >{
      console.log('call SetState SetState);
      this.setState({
        index: this.state.index + 1
      })
      console.log('state'.this.state.index); / / 1

      console.log('call SetState SetState);
      this.setState({
        index: this.state.index + 1
      })
      console.log('state'.this.state.index); / / 2
    },0);
  }
Copy the code

We wrapped the setTimeout async function and found that setState was executed synchronously.

1.2 Perform setState in native events

componentDidMount(){
    document.body.addEventListener('click'.this.bodyClickHandler); // Bind native events in the lifecycle
}

bodyClickHandler = () = >{
  console.log('Call setState in native event');

  this.setState({
    count: this.state.count + 1
  })

  console.log('count'.this.state.count); / / 1

  console.log('Call setState in native event');

  this.setState({
    count: this.state.count + 1
  })

  console.log('count'.this.state.count); / / 2
}
Copy the code

SetState executed in a native event is also executed synchronously.

Asynchronous case:

  1. SetState in the React lifecycle
  2. React Synthesizes the setState in the event

The reason:

During the React lifecycle and compositing events, React is still in its update mechanism, at which point isBatchingUpdates are true. No matter how many times setState is called, the update is not performed. Instead, the state to be updated is stored in _pendingStateQueue and the component to be updated is stored in dirtyComponent.

When the last update mechanism is complete, for example, all components, the topmost component after didmount, will set isBatchingUpdates to false. The setState accumulated before will be executed

Synchronization:

  1. Perform setState in asynchronous functions
  2. SetState is performed in native events

The reason:

According to the execution mechanism, setState itself is not asynchronous, but when setState is called, if React is in the update process, the current update will be temporarily stored and executed after the last update. This process gives the illusion of asynchrony.

In the life cycle, according to the asynchronous mechanism of JS, the asynchronous function will be temporarily stored and executed after all the synchronous code is executed. At this time, the last update process has been completed, isBatchingUpdates are set to false, according to the above process, and then call setState to execute the update immediately. Get the update results.

Simple to understand: When isBatchingUpdates is true, join the asynchronous queue and execute in the next round; When isBatchingUpdates is false, the asynchronous queue is not added and executed immediately. Think of it as a JavaScript EventLoop mechanism.

This is the source code for React-16.6.0

if(! isBatchingUpdates && ! isRendering) { performSyncWork(); }Copy the code

The performSyncWork method is executed immediately when isBatchingUpdates is false. From the name of this method means “Perform synchronization task”.

In Batch mode, React does not immediately modify the state. Instead, it puts the object into an update queue, extracts the new state from the queue and merges it into the state, and then triggers component updates. This design is mainly intended to improve the performance of UI updates.

SetState merger

The React source code also shows that the first parameter of setState can accept two types, one is an object, the other is a function. Object types merge object.assagin, while function types do not.

Component.prototype.setState = function(partialState, callback) {
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null.'setState(...) : takes an object of state variables to update or a ' +
      'function which returns an object of state variables.',);this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Copy the code

Let’s look at how enqueueSetState is handled in the source code:

for (let i = oldReplace ? 1 : 0; i < oldQueue.length; i++) {
  let partial = oldQueue[i];
  let partialState =
      typeof partial === 'function'
  ? partial.call(inst, nextState, element.props, publicContext)
  : partial;
  if(partialState ! =null) {
    if (dontMutate) {
      dontMutate = false;
      nextState = Object.assign({}, nextState, partialState);
    } else {
      Object.assign(nextState, partialState); }}}Copy the code

This is the source code snippet that handles the State Queue, so let’s just focus on it

if(partial === 'function'){
  partial.call(inst, nextState, element.props, publicContext)
}
if(partialState ! =null) {
  Object.assign(nextState, partialState);
}

Copy the code

If the state in the queue is a function, it is executed directly; if it is non-function and not null, object merge is performed first.

Let’s test to see if the incoming function really does not merge

componentDidMount() {
    console.log('SetState passed in function ');

    this.setState((state, props) = > ({
      index: state.index + 1
    }));

    console.log('state'.this.state.index); / / 0

    console.log('SetState passed in function ');

    this.setState((state, props) = > ({
      index: state.index + 1
    }));

    console.log('state'.this.state.index); / / 0

    setTimeout(() = >{
      console.log('state'.this.state.index); / / 2
    },0);
  }
Copy the code

As you can see from the result, we update index twice at the same time, indicating that the passed function will not be merged.

State must be immutable

The add method above is used as an example.

  add = () = >{
    this.state.count++;
    this.setState({
      count: this.state.count
    })
  }
Copy the code
  1. Let’s start by directly changing the count value in the state object
  2. And then we go to setState

We found the same effect, does that mean we can do it this way? The answer is no.

You have to write:

 add = () = >{
    this.setState({
      count: this.state.count + 1})}Copy the code

Immutable values in objects and arrays:

import React from 'react'

class StateDemo1 extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      list:[
        {
          id:1.name:"a"
        },
        {
          id:2.name:"b"
        },
        {
          id:3.name:"C"}}},]render() {
    return (
      <div>
        <ul>
          {
            this.state.list.map((item)=>{
              return <li key={item.id}>
                <span>{item.name}</span>
              </li>})}</ul>
        <button onClick={this.deletePop}>Delete the last item</button>
      </div>)}}export default StateDemo1
Copy the code

When we need to delete this list, this.state.list must follow immutable values.

  deletePop = () = >{
    const newList = [...this.state.list];
    newList.pop();
    this.setState({
      list: newList
    })
  }
Copy the code
  • First of all,this.state.listShallow copy
  • Now I’m going to manipulate the new list array
  • Do the assignment in setState

Instead of changing the state. List value, we made a shallow copy of it and then manipulated it. This is the “immutable value” to watch out for in React programming.

As for why it is necessary to keep values immutable, this has a lot to do with React performance optimizations, which will be explained in more detail in the next article.

The React event

Synthetic events

The Virtual DOM exists in memory as objects, and it is very simple to add events to these objects. React implements a SyntheticEvent layer based on the Virtual DOM. The event handler we define receives an instance of a SyntheticEvent object that complies with W3C standards. There won’t be any COMPATIBILITY issues with IE standards. It has the same interface as the native browser event and supports bubblings for the event, we can use stopPropagation() and preventDefault() to interrupt it.

An implementation mechanism for synthetic events

Under React, two main things are done for composite events: event delegation and automatic binding.

1. Event delegate

Before using React events, be sure to familiarize yourself with its event broker mechanism. Instead of tying event handlers directly to the actual node, it binds all events to the outermost layer of the structure, using a unified event listener that maintains a map to hold event listeners and handlers inside all components. When a component is mounted or unmounted, only objects are inserted or removed from this unified event listener; When an event occurs, it is first handled by the unified event listener, and then the actual event handler is found in the map and called. This simplifies event handling and recycling mechanisms, and improves efficiency.

2. Automatic binding

In the React component, the context of each method points to an instance of that component, which automatically binds this to the current component. React also caches these references to optimize CPU and memory. In fact, React’s synthetic event system is just a subset of the native DOM event system. It only implements the DOM Level 3 event interface and consolidates compatibility issues between browsers. Some React events are not implemented or cannot be implemented due to certain limitations, such as the Window resize event.

For scenarios where events cannot be synthesized using React, we also need to use native events.

Why manual bindingthis

An ES6 class with this inside a method points to an instance of the class by default. You must be careful, however, that you may get an error if you use this method alone. The React event callback method is placed in a queue that executes the corresponding callback when the event is triggered. Therefore, the event callback method is not tied to the React component instance, so we need to manually bind the context.

Bind this binding

import React from 'react'

class EventDemo extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      count: 0}}add(){
    this.setState({
    	count: this.state.count + 1})}render() {
    return <div>
      <p>{this.state.count}</p>
      <button onClick={this.add.bind(this)}>increase</button>
    </div>}}Copy the code

Or bind this in constructor

 constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
    this.add = this.add.bind(this);
  }
Copy the code

Arrow function

Using the arrow function eliminates the need to bind this

  add = () = >{
    this.setState({
    	count: this.state.count + 1})}Copy the code

The difference between a composite event and a native event

  1. React events are named with a hump instead of all lowercase;
  2. Blocking native event propagation requires the use of e.preventDefault(), but for browsers that do not support this method (below Internet Explorer 9), you can only block it with e.canelbubble = true. For React compositing events, just use e.preventDefault();
  3. React implements its own event mechanism, emulates event bubbling and capturing, uses event proxies, batch updates, and smooths out browser compatibility issues.

ReactThe execution order of events and native events

import React from 'react'

class EventDemo extends React.Component {
  constructor(props) {
    super(props);
    this.parent = React.createRef();
    this.child = React.createRef();
  }
  componentDidMount() {
    this.parent.current.addEventListener('click'.(e) = > {
      console.log('dom parent');
    })
    this.child.current.addEventListener('click'.(e) = > {
      console.log('dom child');
    })
    document.addEventListener('click'.(e) = > {
      console.log('document');
    })
  }

  childClick = (e) = > {
    console.log('react child');
  }

  parentClick = (e) = > {
    console.log('react parent');
  }

  render() {
    return (
      <div onClick={this.parentClick} ref={this.parent}>
        <div onClick={this.childClick} ref={this.child}>
          test Event
        </div>
      </div>)}}export default EventDemo
Copy the code

Execution Result:

dom child
dom parent
react child
react parent
document
Copy the code

We can understand from the above process:

  • ReactAll events are mounted indocumentIn the
  • Bubbles to when the real DOM is triggereddocumentLater onReactEvent Processing
  • So native events are executed first
  • Then performReactSynthetic events
  • Finally the execution is really indocumentMount events on

Can React events and native events be mixed?

React events and native events should not be mixed.

If the stopPropagation method is executed in the native event, other React events will become invalid. Because all the elements’ events will not bubble up to the document.

Controlled components and uncontrolled components

Managed and uncontrolled components are primarily for form elements

The controlled components

React handles forms like this:

import React from 'react'

class FormDemo extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      name: 'frank'}}render() {
    return <div>
        <p>{this.state.name}</p>
        <input id="inputName" value={this.state.name} onChange={this.onInputChange}/>
    </div>
  }
  onInputChange = (e) = > {
    this.setState({
      name: e.target.value
    })
  }
}

export default FormDemo
Copy the code

Why is bound to a change event?

In HTML, form elements such as ,

(1) You can set the default value of the form in the initial state. (2) Call the onChange event handler whenever the value of the form changes. (3) The event handler gets the changed state by synthesizing the event object E and updates the state of the application. (4) setState triggers the re-rendering of the view to complete the update of the form component value.

Uncontrolled component

Let’s start with some code:

class FormDemo1 extends React.Component {
  constructor(props) {
    super(props)
    this.content = React.createRef();
  }
  handleSubmit=(e) = >{
    e.preventDefault();
    const { value } = this.content.current;
    console.log(value);
  }

  render() {
    return <form onSubmit={this.handleSubmit}>
      <input ref={this.content} type="text" defaultValue="frank" />
      <button type="submit">Submit</button>
    </form>}}Copy the code

In React, an uncontrolled component is an anti-pattern whose value is not controlled by the component’s own state or props. Typically, you need to access the rendered underlying DOM element by adding a REF to it.

The biggest difference between a controlled component and an uncontrolled component is that the state of an uncontrolled component is not controlled by the application state, whereas the value of a controlled component comes from the state of the component.

summary

React is a very important part of the interview process, so the React stack interview article will be divided into several sections