react Basics

1 understanding of react

Advantages:

  • Efficient and flexible
  • Declarative design, easy to use
  • Component development to improve code reuse rate
  • One way response data flow is safer and faster than two-way binding

2 real dom and virtual dom

jsx:

  • JSX is actually a syntax sugar, which will be compiled and converted into JS code by babel during use, which is equivalent to react Createelement() method.

  • ReactDOM.render() is used to insert the created virtual DOM node into a real node and render it to the page.

difference:

  • Virtual DOM will not rearrange and redraw, while real DOM will rearrange and redraw frequently;
  • The total loss of virtual DOM is "virtual DOM addition, deletion and modification + real DOM difference addition, deletion and modification + typesetting and redrawing", and the total loss of real DOM is "real DOM complete addition, deletion and modification + typesetting and redrawing".

3 life cycle

New edition:

Old version:

Creation phase:

constructor:

  • The method automatically called during the instance process obtains props from the parent component through the super keyword inside the method

  • In this method, the usual operation is to initialize the state state or mount the method on this

componentWillMount:

  • Only once, the component is invoked before being rendered to the real dom for the first time, and render will be called once the execution is completed.

render:

  • Class component must implement the method, which is used to render the DOM structure, and can access the component state and prop properties

  • Note: do not set state in render, otherwise it will trigger an endless loop and cause memory crash

componentDidMount:

  • The component is executed after it is mounted to the real DOM node, which is executed after the render method

  • This method is mostly used to perform some operations such as data acquisition and event listening

Update phase:

shouldComponentUpdate:

  • It is used to tell the component whether it needs to re render the component based on the current props and state. By default, it returns true

  • Execution timing: it will be called when a new props or state is reached. It will tell whether the component is updated or not by returning true or false

  • Generally, it is not recommended to make deep comparison in this periodic method, which will affect the efficiency

  • At the same time, setState cannot be called, otherwise it will cause infinite loop call update

Unloading phase:

componentWillUnmount:

  • This method is used to clean up some registration events, listen to events, or unsubscribe network requests before component unloading

  • Once a component instance is uninstalled, it will not be mounted again, but can only be recreated

4 state and props

Similarities:

  • Both are JavaScript objects
  • Both are used to save information
  • Both props and state can trigger rendering updates

difference:

  • props is passed externally to the component, while state is managed by the component itself in the component, which is generally initialized in the constructor
  • props cannot be modified inside the component, but state can be modified inside the component
  • state is changeable and can be modified

Class component, super(props) binds this to the sub component, and then you can use this Props access; In the function component, you can directly function Page(props).

setState() asynchronous synchronous execution

5 component communication

(1) Parent component = > child component

Pass the parameters in the child component label, and the child component can receive the parameters passed by the parent component through the props attribute

function EmailInput(props) {
  return (
    <label>
      Email: <input value={props.email} />
    </label>
  );
}

const element = <EmailInput email="123124132@163.com" />;

(2) Child component = > parent component

The parent component passes a function to the child component, and then gets the value passed by the child component through the callback of this function

The corresponding codes of parent components are as follows:

class Parents extends Component {
  constructor() {
    super();
    this.state = {
      price: 0
    };
  }

  getItemPrice(e) {
    this.setState({
      price: e
    });
  }

  render() {
    return (
      <div>
        <div>price: {this.state.price}</div>
        {/* Pass a function into the subcomponent  */}
        <Child getPrice={this.getItemPrice.bind(this)} />
      </div>
    );
  }
}

The corresponding codes of sub components are as follows:

class Child extends Component {
  clickGoods(e) {
    // Pass in a value in this function
    this.props.getPrice(e);
  }

  render() {
    return (
      <div>
        <button onClick={this.clickGoods.bind(this, 100)}>goods1</button>
        <button onClick={this.clickGoods.bind(this, 1000)}>goods2</button>
      </div>
    );
  }
}

(3) Communication between sibling components

The parent component is used as the middle layer to realize data interworking, which is transmitted by using the parent component

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {count: 0}
  }
  setCount = () => {
    this.setState({count: this.state.count + 1})
  }
  render() {
    return (
      <div>
        <SiblingA
          count={this.state.count}
        />
        <SiblingB
          onClick={this.setCount}
        />
      </div>
    );
  }
}

(4) Transfer from parent component to descendant component

It is the most common thing for a parent component to transfer data to a descendant component, just like global data

Using context provides a way of communication between components, which can share data, and other data can read the corresponding data

By using react Createcontext creates a context

 const PriceContext = React.createContext('price')

After the context is created successfully, the Provider component is used to create the data source and the Consumer component is used to receive the data. The use examples are as follows:

The Provider component is used to pass data to descendant components through the value attribute:

<PriceContext.Provider value={100}>
</PriceContext.Provider>

If you want to get the data passed by the Provider, you can receive it through the Consumer component or by using the contextType attribute. The corresponding are as follows:

class MyClass extends React.Component {
  static contextType = PriceContext;
  render() {
    let price = this.context;
    /* Render based on this value */
  }
}

Consumer component:

<PriceContext.Consumer>
    { /*Here is a function*/ }
    {
        price => <div>price: {price}</div>
    }
</PriceContext.Consumer>

(5) Non relational component delivery

If the relationship types between components are complex, it is recommended to conduct a global resource management for the data to realize communication, such as redux.

6 events

The execution order of events (synthetic events & native events) e.preventDefault() prevents event bubbling

To solve the problem of outputting this correctly, common binding methods are as follows:

  • Use bind in render method
  • Use arrow function in render method
  • bind in constructor
  • Arrow function binding is used in the definition phase (in common use, it is recommended that both function components and class components be written in this way)

7 components

Common component creation methods:

  • Functional creation
  • Through react Createclass method creation
  • Inherit react Component creation

Controlled and uncontrolled components:

  • Controlled components generally need an initial state (such as value) and a state update event function (such as onChange)

  • Most of the time, it is recommended to use the controlled component to implement the form, because in the controlled component, the form data is processed by the React component

  • If the uncontrolled component is selected, the control ability is weak, and the form data is processed by the DOM itself, but it is more convenient and faster with less code

8 react refs

Refs in React provides a way to access DOM nodes or React elements created in the render method.

The essence is reactdom Render() returns the component instance. If it is a rendering component, it returns the component instance. If it is a rendering dom, it returns the specific dom node.

use

  • Incoming string
  • Incoming object
  • Incoming function
  • Incoming hook, useRef()

Application scenario

  • When updating components, props and state are generally used, because excessive use of refs will expose the instance or DOM structure of components and violate the principle of component encapsulation.

  • In some scenarios, ref is generally used:

    • Control of DOM element focus, content selection or media playback;

    • Trigger animation effects by controlling DOM elements;

    • Through the integration of third-party DOM libraries.

React.forwardRef() and higher-order components

this.myRef = React.createRef(); // establish
const node = this.myRef.current; // visit

Some knowledge points

(1) Add a ref to the dom element

Access the DOM node through "current"

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // Create a ref to store the DOM element of textInput
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // Use the native API directly to focus the text input box
    // Note: we access the DOM node through "current"
    this.textInput.current.focus();
  }

  render() {
    // Tell React that we want to associate < input > ref to
    // On the 'textInput' created in the constructor
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}
(2) Add ref to class component
class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    // Create a ref pointing to the CustomTextInput component instance
    this.textInput = React.createRef();
  }

  componentDidMount() {
    // Call the subcomponent focusTextInput method to trigger the internal text box of the subcomponent to obtain the focus event
    this.textInput.current.focusTextInput();
  }

  render() {
    return (
      <CustomTextInput ref={this.textInput} />
    );
  }
}

When the ref attribute is used to customize a class component, the ref object receives the mounted instance of the component as its current attribute.

(3) Function components and ref

By default, the ref attribute cannot be used on function components because they have no instances;

However, you can use the ref attribute inside a function component as long as it points to a DOM element or class component.

function CustomTextInput(props) {
  // textInput must be declared here so that ref can reference it
  const textInput = useRef(null);

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}
(4) ref forwarding: get the dom node of the sub component

You can access props, state, refs and instance methods (not inherited methods) of the sub component instance through the ref of the sub component.

Access to sub components using ref:

  • Access a specific dom node of a sub component to complete some logic through this refs. childComponentRefName. refs. Somedomrefname, for example How does the React parent component get the ref value of the child component?.
  • You can access the public instance method of the sub component to complete a write logic. For example, the child component defines a reset method to reset the form element value of the child component. In this case, the parent component can use this refs. childComponentRefName. Reset () to reset the form elements of the subcomponent.
(5) Callback refs

Pass a function. This function accepts React component instances or HTML DOM elements as parameters so that they can be stored and accessed elsewhere.

// Use the ref callback function to store a reference to the DOM node in the properties of the instance
class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // Use the native DOM API to focus the text input box
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    // After the component is mounted, let the text box automatically get the focus
    this.focusTextInput();
  }

  render() {
    // Use the callback function of 'ref' to store the reference of the DOM node of the text input box in React
    // Instance (such as this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

React will call the ref callback function and pass in the DOM element when the component is mounted, and call it and pass in null when the component is unloaded. Before componentDidMount or componentDidUpdate is triggered, react will ensure that refs must be up-to-date.

refs in the form of callback can be passed between components.

9 context

Context provides a method to transfer data between component trees without manually adding props for each layer of components.

Context is designed to share data that is "global" to a component tree, such as currently authenticated users, topics, or preferred languages.

use

// Context allows us to pass values into the component tree without explicitly passing them through each component.
// Create a context for the current theme ("light" is the default).
const ThemeContext = React.createContext('light');      // Key points
class App extends React.Component {
  render() {
    // Use a Provider to pass the current theme to the following component tree.
    // No matter how deep, any component can read this value.
    // In this example, we pass "dark" as the current value.
    return (
      <ThemeContext.Provider value="dark">      // Key points
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// The middle component no longer has to indicate that the theme is passed down.
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // Specify contextType to read the current theme context.
  // React will go up to find the nearest theme Provider and use its value.
  // In this example, the current theme value is "dark".
  static contextType = ThemeContext;      // Key points
  render() {
    return <Button theme={this.context} />;    // Use this Context to consume the value on the latest context
  }
}

The main application scenario of Context is that many components at different levels need to access the same data.

API

React.createContext:
const MyContext = React.createContext(defaultValue);

When React renders a component subscribed to the context object, the component will read the current context value from the matching Provider closest to itself in the component tree.

Context.Provider:
<MyContext.Provider value={/* A value */}>

The Provider receives a value attribute and passes it to the consumer component.

When the value value of the Provider changes, all its internal consumer components will be re rendered.

Class.contextType:
class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;   // Key points
    /* After the component is mounted, use the value of the MyContext component to perform some operations with side effects */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* Render based on the value of the MyContext component */
  }
}
MyClass.contextType = MyContext;

The contextType attribute mounted on the class will be reassigned to a React.createContext() Create a new context object. This allows you to use this Context to consume the value on the latest context.

Context.Consumer:
<MyContext.Consumer>
  {value => /* Rendering based on context value*/}
</MyContext.Consumer>
Context.displayName:
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider> // "MyDisplayName.Provider" is in DevTools
<MyContext.Consumer> // "MyDisplayName.Consumer" is in DevTools

The context object accepts a property named displayName of type string. React DevTools uses this string to determine what the context will display.

application

Dynamic context
Update context in nested components

Pass a function through the context to make the consumers component update the context.

Consume multiple context s

10 typeScript

Definition and use of interfaces

Union type|

Type assertion??? Type inference

Optional attribute: attribute name is followed by a?

Index signature: defines the data structure of key (propName) and value in the object. As long as the key and value meet the limit of index signature, it doesn't matter how many there are

interface FullName{
    firstName:string
    lastName:string
    middleName?:string // optional attribute 
    [propName:string]:any // Index signature
}

? Operator:

  1. Optional parameters
// there? Indicates that this parameter field is an optional parameter
function getUser(user: string, field?: string) {
}
  1. member
// there? Indicates that the name attribute may not exist
class A {
  name?: string
}

interface B {
  name?: string
}
  1. Security chain call
// Here, the stack defined by the Error object is an optional parameter. If it is written in this way, the compiler will prompt
// Error ts2532: object is probably 'undefined'
return new Error().stack.split('\n');

// Can we add? Operator, when the stack attribute exists, call stack split. 
// If stack does not exist, null is returned
return new Error().stack?.split('\n');

// The above code is equivalent to the following code. Thank @ dingyanhe for his supervision
const err = new Error();
return err.stack && err.stack.split('\n');

! Operator:

  1. Unary operator
// !  Is to reverse the subsequent results, such as:
// Returns False when isNumber(input) is True; 
// Returns True when isNumber(input) is False
const a = !isNumber(input);
  1. member
// Because the name in interface B is defined as a nullable value, but the actual situation is not empty,
// Then we can use it in class!, Re emphasize that name is not null
class A implemented B {
  name!: string
}

interface B {
  name?: string
}

Here you can refer to ts more stringent class attribute checking

Typescript 2.7 introduces a new strict control tag - strictPropertyInitialization, which is set in tsconfig Configuration in TS

"strictNullChecks": true
"strictPropertyInitialization": true

effect

  • Using this tag ensures that each instance property of the class is assigned in the constructor or using the property initializer.
  • It explicitly checks the assignment from the variable to the instance property of the class

give an example

class C {
  foo: number;
  bar = "hello";
  baz: boolean;
  constructor() {
    this.foo = 42;
  }
}

For the above code, the editor will first report an error: the attribute "baz" has no initialization expression and is not explicitly assigned in the constructor. ts(2564).
Secondly, an error is reported during compilation: error ts2564: property 'Baz' has no initializer and is not definitively assigned in the constructor

Both tell developers that they should assign a value to baz display, but in some cases, we don't want to assign a value during initialization. We expect it to be undefined, and then assign a value. At this time!: It comes in handy.

Add! Before the attribute baz colon in the above code, So you won't report an error

class C {
  foo: number;
  bar = "hello";
  baz!: boolean;
  constructor() {
    this.foo = 42;
  }
}
  1. Force chain call
// Here, the stack defined by the Error object is an optional parameter. If it is written in this way, the compiler will prompt
// Error ts2532: object is probably 'undefined'
new Error().stack.split('\n');

// If we are sure that this field appears 100%, we can add it!, Emphasize that this field must exist
new Error().stack!.split('\n');

Type Asserts

11 redux

Core concept

State

state is a collection of data.

action

The change of State will lead to the change of View. However, users can't touch the State, they can only touch the View. Therefore, the change of State must be caused by View.

action is the instruction to change the state. There will be as many actions as there are actions to operate the state.

reducer

After the action sends the command, put the state into the reucer processing function and return the new state.

store
let store = createStore(reducers);

Store is the object that connects them. Store has the following responsibilities:

Maintain application state;
provide getState() Method acquisition state;
provide dispatch(action) Method update state;
adopt subscribe(listener) Register listener;
adopt unsubscribe(listener) The returned function unregisters the listener.

We can use store Getstate() to understand the status of goods in the factory, using store Dispatch() sends the action instruction.

example:

import { createStore } from 'redux'

// The reducer processing function accepts two parameters, state and action, and action has a type attribute
const reducer = (state = {count: 0}, action) => {
  switch (action.type){
    case 'INCREASE': return {count: state.count + 1};
    case 'DECREASE': return {count: state.count - 1};
    default: return state;
  }
}

const actions = {
  increase: () => ({type: 'INCREASE'}),
  decrease: () => ({type: 'DECREASE'})
}

// Create store
const store = createStore(reducer);

// monitor
store.subscribe(() =>
  console.log(store.getState())
);

// Send the action instruction to update the state
store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}

react-redux

core
  • < Provider store>
  • connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

If any component in the Provider (such as Comp here) needs to use the data in the state, it must be a "connected" component - the product of packaging the "component you write (MyComp)" using the connect method.

This function allows us to bind the data in the store to the component as props.

The connect method in react Redux wraps the getState and dispatch on the store into props of the component.

example
// ****************************** root.js ******************************

// Create a store to manage state and action globally
const store = createStore(reducer);

// The Provider has a layer outside the root component < App >, and all sub components of the App can get the store by default and pass it through the props of the component
export default class Root extends Component {
    render() {
        return (
            <Provider store={store}>           // Key points
                <App/>
            </Provider>
        )
    }
}



// ****************************** app.js ******************************

// view provides UI components
class Counter extends Component {
  render() {
    // The state of View comes from props, which passes the state in the store
    const { value, onIncreaseClick } = this.props           // Key points
    return (
      <div>
        <span>{value}</span>
        <button onClick={onIncreaseClick}>Increase</button>
      </div>
    )
  }
}

// Connect the UI component to the container component
const App = connect(                             // Key points
 state =>({
    value: state.count   //Enter the state in the store through props
 }),
 dispatch =>({    // Output, bind the action to the View as props, and distribute the user operation type here
    onIncreaseClick: () => dispatch(increaseAction.increase())
  })
)(Counter)

export default App



// ****************************** increaseAction.js ******************************

// Define the type of action
export const INCREMENT = 'INCREMENT';

// The action creation function simply returns an action
export function increase(params) {
    return {
        type: INCREMENT,
        data:data
        };
}

 

// ****************************** counterReducer.js ******************************

// Provide an initial state
initState={
 count: 0 
}

// By judging the type of Action, return the state object after new data changes. Even if there is no change in state, return an object
export default function counter(state = initState, action) {
  const count = state.count
  switch (action.type) {
    case INCREMENT:
      return { count: count + 1 }
    default:
      return state
  }
}

rematch

Step 1: Init

init is used to configure your reducers, devtools & store.

// ****************************** index.js *****************************
import { init } from '@rematch/core'
import * as models from './models'

const store = init({
  models,
  //plugins: [selectPlugin(), persistPlugin, trackPlugin],
  //redux: {
  //  devtoolOptions: {
  //    disabled: !__DEV__
  //  }
  //}
})

export default store
Step 2: Models

The model enables state, reducers, async actions and action creators to be placed in the same place.

// ****************************** models.js *****************************
import { createModel } from '@rematch/core';

const model = {
  state: 0, // initial state
  reducers: {
    // handle state changes with pure functions
    increment(state, payload) {
      return state + payload
    }
  },
  effects: {
    // handle state changes with impure functions.
    // use async/await for async actions
    async incrementAsync(payload, rootState) {
      await new Promise(resolve => setTimeout(resolve, 1000))
      this.increment(payload)
    }
  }
}

export default createModel(model);
Step 3: Dispatch

Dispatch is how we trigger reducers and effects in your model. Dispatch standardizes your actions without writing action types or action creators.

import { dispatch } from '@rematch/core'
                                                  // state = { count: 0 }
// reducers
dispatch({ type: 'model/increment', payload: 1 }) // state = { count: 1 }
dispatch.model.increment(1)                       // state = { count: 2 }

// effects
dispatch({ type: 'model/incrementAsync', payload: 1 }) // state = { count: 3 } after delay
dispatch.model.incrementAsync(1)                       // state = { count: 4 } after delay

Dispatch can be called directly or abbreviated as dispatch[model][action](payload).

Step 4: View

Provider and connect. The connect method wraps the getState and dispatch on the store into props of the component.

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider, connect } from 'react-redux'
import store from './index'

const Count = props => (
  <div>
    The count is {props.count}       // Through props XXX access
    <button onClick={props.increment}>increment</button>
    <button onClick={props.incrementAsync}>incrementAsync</button>
  </div>
)

const mapState = state => ({
  count: state.model
})

const mapDispatch = ({ count: { increment, incrementAsync }}) => ({
  increment: () => increment(1),
  incrementAsync: () => incrementAsync(1)
})

const CountContainer = connect(mapState, mapDispatch)(Count)

ReactDOM.render(
  <Provider store={store}>
    <CountContainer />
  </Provider>,
  document.getElementById('root')
)

After using Rematch, we can use store dispatch. model. Xxx() to execute an Action operation, and store The type of dispatch is automatically calculated by TS and does not need to be manually defined by the developer.

export type Dispatch = typeof store.dispatch;

export default connect(null, (dispatch: Dispatch) => ({
  showTip: dispatch.bbsMainTab.showTip,
  hideTip: dispatch.bbsMainTab.hideTip
}))(MainTab);

RematchRootState,createModel

Pay attention to the usage of typeof in ts
Xxx<typeof {name:'Golden hair', age:9}>

// This is equivalent to:

Xxx<typeof {name:string, age:number}>
  • [modelKey in keyof M] cycle all the key values in the m object. Each cycle is named modelKey.

  • M[modelKey] is to take out the corresponding value, which specifically refers to the type value in ts.

Keywords: React

Added by Sonu Kapoor on Wed, 26 Jan 2022 06:23:14 +0200