When I first started with a front-end framework, I chose Vue, which was faster to use; So far, I have developed several projects based on Vue, and I have a certain understanding of the principle of Vue, which can be said to be “proficient in use” ~ ๐ŸŒ Recently the project is not too busy, I finally… Decided to learn React!! (Rubbing hands, excited, ๐Ÿ‘‹๐Ÿผ)

โคด๏ธ How to get started: how to get started on the official website + Choose a review of React book ๐Ÿ“š

Preparation before starting

React features: virtual DOM, state, one-way data flow, components

  • Declarative view layer – a combination of JSX, HTML and JS
  • Simple update process — developers only define UI state, React renders
  • Flexible rendering implementation – The virtual DOM can be rendered to different terminals in conjunction with other libraries
  • Efficient DOM manipulation — virtual DOM

Start the React project:

npm install -g create-react-app // Install create-react-app scaffolding
create-react-app my-app // Create the project
cd my-app/ 
npm start / / start
Copy the code

Check: create-react-app.dev/docs/gettin…

Compatibility problem node version switching:

sudo n v1415.. 0 // A version number
Copy the code

First, some basic concepts

1, JSX

  • WHAT?

JSX is a syntactic extension of Javascript, JSX = Javascript + XML, that is, to write XML in Javascript, because of this feature of JSX, so it has the flexibility of Javascript, At the same time, it is both semantic and intuitive of HTML.

Such as:

function Title(){
  return <h1>Im title~~~</h1>;
}
Copy the code

In React, JSX can generate React “elements”. The React element will be explained later.

  • According to?

React argues that rendering logic is intrinsically coupled to other UI logic, such as binding events in the UI, notifying the UI when state changes at certain moments, and displaying prepared data in the UI. React implements separation of concerns by storing both together in loosely coupled units called components.

Different from the way VUE separates JS and HTML

  • HOW?
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>; // {} can write JS expressions inside

ReactDOM.render(
  element,
  document.getElementById('root'));// Render the JSX element to the element whose id is root
Copy the code

Suggestion: If JSX has multiple lines, enclose them in parentheses.

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

Babel translates JSX into a function call called react.createElement () that creates the corresponding DOM object:

// This is the simplified structure
const element = {
  type: "h1".props: {
    className: "greeting".children: "Hello, world!",}};Copy the code

These objects are called React elements. — [JS object]

Note: JSX writes native DOM attributes as className and event names as camel (onclick -> onclick).

2. Element rendering

To render a React element into a DOM node, simply pass them together to reactdom.render (). If the UI is to be updated, reactdom.render () needs to be called again. React only updates the changed part — the virtual DOM & Diff — when re-rendering.

Components & props

There are two ways to write components in React: functions and classes. The first letter of the component must be capitalized.

Function component: if the component only has a render method and no state, it is simpler to write function component.

function MyComp(props) {
  return <h1>hello {props.name}</h1>;
}
Copy the code

Class component (equivalent) :

/ / inheritance in React.Com ponent
class MyComp extends React.Component {
  constructor(props) {
    super(props); // props is the argument received by the component, and super means to execute the constructor of the parent class to complete initialization
  }
  render() {
    // The render method returns the view structure to display -- the React element
    return <h1>hello {this.props.name}</h1>; }}Copy the code

In all React components that have constructors, constructors must start with super(props). Otherwise, undefined bugs may occur in the this.props constructor.

Class components must meet two conditions:

  • classInherited fromReact.Component;
  • classInternal must be definedrenderMethods;
function App() {
  return (
    <div>
      <MyComp name="A" />{/* The child component will receive */} via this.props. Name<MyComp name="B" />
      <MyComp name="C" />
    </div>
  );
}
Copy the code

When passed to the child component, the child component gets a PROPS object.

Props is the way a parent component passes a value to a child component! It is read-only: All React components must protect their props from being changed like pure functions. We can’t change props directly in a subcomponent.

React provides the PropTypes object for verifying props.

import PropTypes from "prop-types";

PropTypes.propTypes = {
  a: PropTypes.object, The a attribute is an object type
  b: PropTypes.number, // the b attribute is a numeric type
  c: PropTypes.func.isRequired, // Function type, required
};
// defaultProps specifies the default value for the property
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
Welcome.defaultProps = {
  name: "World"};Copy the code

Component styles: external CSS and inline styles

  • tag introduction: applies to all components
  • Import introduces: scoped local styles
  • Inline style:<div style={{color: 'red'}}></div>The first {} indicates that it is a JS expression, the second {} indicates that it is an object inside, and the property name must be in camel form.

4. Life cycle

โš ๏ธ Only class components have lifecycle methods, not function components

The life cycle includes:

  • Mount phase, call:
constructor() / /classConstructor method, in whichsuper(props) receiving parameterscomponentWillMount(// The component is called before it is mountedrender(// returns a method defined in the componentReactElement is not responsible for the actual renderingcomponentDidMount() // The component is mounted toDOMCall after, for example, when the back end requests some datasetStateCauses the component to be rerenderedCopy the code
  • During the update phase, the props or state of the component changes and is called in sequence:
componentWillReceiveProps(nextProps) // called when props changes, nextProps is the new parameter
shouldComponentUpdate(nextProps, nextState) // Whether to continue the update process, return a Boolean value
// By comparing the old and new values, the method returns false if the old and new values are the same, and subsequent updates are not continued to optimize performance
componentWillUpdate(nextProps, nextState) // Before the update, it was rarely used
render()
componentDidUpdate(prevProps, prevState) // After the component is updated, you can manipulate the updated DOM
Copy the code
  • Unloading stage:
componentWillUnmount(){}// Called before the component is deleted to do some cleanup
Copy the code

5, the state

You can set state in a component to store the component’s own data:

class MyComp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date()}; }render() {
    return <h1>Its {this.state.time}</h1>; }}Copy the code

Instead of changing state directly, use setState(). React will merge the state we set. React automatically updates its children every time setState is called in a component.

Data flow is one-way ~ only from parent to child.

Immutability: In general, there are two ways to change data. The first way is to modify the value of the variable directly, and the second way is to replace the old data with a new one. React recommends using the second method. Why?

  • Simplify complex functions

Immutability makes complex features easier to implement. For example — undo and restore is a common requirement in development, and not directly modifying the data allows us to trace and reuse the history.

  • Track changes in data

If you modify the data directly, it is difficult to track the changes. Tracking data changes requires that the mutable object can be compared to the previous version, so that the entire object tree needs to be traversed once.

It is relatively easy to track changes in immutable data. If the object becomes a new object, we can say that the object has changed.

  • Determine when to rerender in React

The main advantage of immutability is that it helps us create pure Components in React. We can easily determine if immutable data has changed and thus when to rerender the component.

6. Event handling

React events are named in camel case like this:

<button onClick={activateLasers}> Activate Lasers </button>
Copy the code

Response functions that handle events are assigned to event properties as objects, not strings. Because events in React are synthetic events, not native DOM events.

React has a naming convention that typically names the listener prop representing the Event as on[Event] and the listener method that handles the Event as handle[Event].

Note that this points to the problem in React event handling:

(1) Arrow function

class MyComp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date()}; }handleClick() {
    console.log(this.state.time);
  }
  render() {
    return (
      <button
        onClick={()= >{ this.handleClick(); }} > button</button>); }}// This points to the instance object of the current component
Copy the code

This way, a new event handler is recreated each time Render is called.

(2) Component methods

class MyComp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date()};this.handleClick = this.handleClick.bind(this);
    // Bind this method to the current component instance
  }
  handleClick() {
    console.log(this.state.time);
  }
  render() {
    return <button onClick={this.handleClick}>button</button>; }}Copy the code

This way, render calls do not recreate a new event handler, but manually bind this in the constructor.

Alternatively, we could bind this at the same time we assign to the element event attribute:

return <button onClick={this.handleClick.bind(this)}>button</button>
Copy the code

(3) Attribute initialization syntax (ES7)

class MyComp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      time: new Date()}; } handleClick =() = > {
    console.log(this.state.time);
  }; // also arrow function
  render() {
    return <button onClick={this.handleClick}>button</button>; }}Copy the code

Projects created using the official scaffolding Create React App support this feature by default. You can use Babel’s transform-class-properties plugin to support this feature.

Conditional rendering & Lists & Forms

Conditional rendering:

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

List:

const numbers = [1.2.3.4.5];
const listItems = numbers.map((number) = >  <li>{number}</li>);
Copy the code

It is recommended that each dynamic list element be given a key: [similar to Vue], otherwise the console will report an error

const listItems = numbers.map((number) = > (
  <li key={number.toString()}>{number}</li>
));
Copy the code

Forms: If a form element’s value is managed by React, it is called a controlled component.

1) Text fields — Input elements and Textarea elements of type Text are controlled by setting the value of the form element through its value attribute and listening for changes in value through the onChange event of the form element. And synchronize the changes to the state of the React component.

render(){
    return (
      <form onSubmit={this.handleSubmit}>
        <label>Name:<input type="text" value={this.state.value} onChange={this.handleChange} />        
         </label>
         <input type="submit" value="Submit" />
      </form>
    );
}
Copy the code

2) Select element — Determines which option element is selected by defining the value attribute on the SELECT.

render() {
    return (
        <select value='2'>
            <option value='1'>1</option>
            <option value='2'>2</option>
            <option value='3'>3</option>
        </select>
    );
}
// The element with value 2 is selected
Copy the code

3) Checkboxes and checkboxes — Input elements with type checkbox and radio, React controls the Checked property.

handleSelectChange(event){
    this.setState({
        [event.target.name]: event.target.checked
    })
}
render() {
    return (
        <label>
          <input type="checkbox" value="yes" name="yes" checked={this.state.yes} onChange={this.handleSelectChange}
          />Yes.</label>
        <label>
          <input type="checkbox" value="no" name="no" checked={this.state.no} onChange={this.handleSelectChange}
         />not</label>
    );
}
Copy the code

Using controlled components to handle form state is cumbersome. An alternative is to use uncontrolled components — the form manages its own state and React fetches the form’s value through a ref. This simplifies operations, but breaks the consistency of React’s component state management, so use it sparingly and we’ll skip it here.

8. State improvement

State promotion refers to the promotion of data maintained by multiple child components to the parent component.

handleChange(e) {
  this.props.onTemperatureChange(e.target.value); // Triggers an event for the parent component
  // Similar to vue's this.$emit
}
Copy the code

When you need to fetch data from multiple child components at the same time, or two components need to communicate with each other, you need to promote the child’s state data to its common parent. The parent component can then pass state data to the child component via props. This makes it easier to synchronize and share state data across all components of the application.

9. Composition & Inheritance

React recommends using compositing to reuse code. Props and combinations give you a flexible way to customize the look and behavior of components in a clear and secure way.

Note: Components can accept any props, including basic data types, React elements, and functions.

2. React Philosophy

One of the best parts of React is that it gets us thinking about how to build an application.

For example, suppose we have a design and an API that returns data:

Step 1: Divide your designed UI into component levels

Box each component, including its children, with appropriate names on the design. Component partitioning principle – Single function principle: a component is responsible for only one function.

Step 2: Create a static version of React

Having identified the component hierarchy, you can write the corresponding application. It is best to keep the processes of rendering the UI and adding interactions separate.

Start by rendering a static UI with an existing data model that contains no interaction.

Step 3: Determine the minimum and complete representation of UI state

Find the minimum set of states your application needs, without duplication or redundancy. That is to say, under the premise of realizing the function, the number of variables should be set as minimum as possible.

For example, the sample application has the following data:

  • Contains the original list of all products
  • The search term entered by the user
  • Check box whether the value is selected
  • A list of products filtered by search

How to get the minimum representation of check state? Ask yourself three questions:

  • Is this data passed by the parent component via props? If so, it should not be state.
  • Has the data remained the same over time? If so, it should not be state either.
  • Can you calculate the value of this data based on other states or props? If it is, it’s not state.

After checking, the ones belonging to state in the previous example are:

  • The search term entered by the user
  • Check box whether the value is selected

Step 4: Determine where state is placed — which component should have a state

The data flow in React is one-way and flows up and down the component hierarchy.

For each state in the application:

  • Find all components rendered according to this state.
  • Find their common owner components (above all components that require this state at the component level).
  • The co-owner component or a higher level component should have this state.
  • If you can’t find a good place to store the state, you can simply create a new component to store the state and place the new component higher than the co-owner component.

Step 5: Add the reverse data flow

Child components pass values to parent components.

No matter what framework you use, think about how to divide components and how to set variables before you start writing code.

React 16 new features

๐Ÿคก React 16 Before the render method had to return a single element, it now supports returning arrays and strings.

class Example extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return "ssss"; }}Copy the code

๐Ÿคก React 16 Before React 16, components blocked the entire application rendering if they failed at runtime. Now there is a new error handling mechanism: By default, when an error is thrown in a component, the component is unloaded from the component tree, preventing the entire application from crashing. React 16 also provides a more user-friendly way of handling errors — Error Boundaries, which are components that can catch and elegantly handle the errors of child components. The elegant process can be to print error logs, display error messages, and so on, which is obviously friendlier than simply uninstalling the component.

The component that defines componentDidCatch(error, info) becomes an error bound:

import React from "react";
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false}; }componentDidCatch(error, info) {
    this.setState({
      hasError: true});console.log(error, info);
  }
  render() {
    if (this.state.hasError) {
      return <h1>OOPS, mistake!</h1>;
    }
    return this.props.children; }}export default ErrorBoundary;
Copy the code

Using ErrorBoundary:

<ErrorBoundary>
  <Example></Example>
</ErrorBoundary>
Copy the code

If the internal component is abnormal, the error will be captured by the ErrorBoundary and a prompt will be displayed on the interface.

The ๐Ÿคก Portals feature: Allows you to render a component to a DOM node outside the current component tree. [Teleport is similar to Vue]

ReactDOM.createPortal(child, container);
// Child is a React element/element array/string that can be rendered
// Container is the DOM node to which the child is mounted
Copy the code

For example, create a Modal component:

import React from "react";
import ReactDOM from "react-dom";

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.container = document.createElement("div");
    document.body.appendChild(this.container);
  }
  conponentWillUnmount() {
    document.body.removeChild(this.container);
  }
  render() {
    return ReactDOM.createPortal(<div>I am a Modal</div>.this.container); }}export default Modal;
Copy the code

This makes it the last child of the body no matter where the component is invoked.

๐Ÿคก React Hooks: When using Class components, there are a lot of business logic such as all kinds of interface requests need to be put in componentDidMount and componentDidUpdate and other life cycle functions, will make the components become particularly complex and difficult to maintain, and this problem in Class also need special attention; Function components, while avoiding this problem, have no lifecycle, etc.

With Hooks in place, various features of React can be used in functional components.

Hooks are essentially special functions, the common ones being:

๐ŸŽฃ useState: useState

const [state, setState] = useState(initState);
// For example
import React, { useState } from "react";
function App() {
  const [state, setState] = useState("Hah");
  // Whatever u need can be renamed by struct assignment
  return (
    <div>
      <h1>{state}</h1>
    </div>
  );
}
export default App;
Copy the code

Here, the useState method is asynchronous, like the same method as the component. But it does not merge multiple states.

๐ŸŽฃ useRef: useRef

import React, { useRef } from "react";
function App() {
  let el = useRef();
  return (
    <div>
      <h1 ref={el}>{state}</h1>
    </div>
  );
  // The DOM node can be obtained from el.current
}
export default App;
Copy the code

๐ŸŽฃ useEffect: Handles side effects such as network requests, DOM operations, etc. Equivalent to componentDidMount and componentDidUpdate, etc.

Here’s an example:

import React, { useState, useEffect } from "react";
function Course() {
  const [name, setName] = useState("React");
  useEffect(() = > {
    console.log("Component mount or update");
    return () = > {
      console.log("Clean up some stuff before the update.");
    };
  }, [name]);
  return (
    <div>
      <select
        value={name}
        onChange={({ target}) = > {
          setName(target.value);
        }}
      >
        <option value="React">React</option>
        <option value="Vue">Vue</option>
        <option value="JQuery">JQuery</option>
      </select>
    </div>
  );
}
export default Course;
Copy the code

As you can see, useEffect takes two arguments, the first a function and the second an array, where the first function returns a function and the second array represents dependent arguments. When a dependency parameter changes, the callback function is executed. The entire component lifecycle process: component mounting โ†’ Performing side effects (callback function) โ†’ component updating โ†’ performing cleaning functions (return function) โ†’ Performing side effects (callback function) โ†’ Preparing component for uninstallation โ†’ Performing cleaning functions (return function) โ†’ Component uninstallation.

If you simply want to perform certain operations in a particular lifecycle, you can do this by passing different parameters:

  • Only in thecomponentDidMountExecute, you can set the dependent parameters to an empty array so that the side effect is not performed during updates.
  • Only in thecomponentWillUnmountExecute, also setting the dependency parameters to an empty array, and the return function for that side effect will be executed before unloading.
  • Only in thecomponentDidUpdateExecution, need update or mount, testing data and the initial values are consistent, if the current data is consistent with the initial data that mount the stage, on the safe side, of course, compared with the previous value, if the current reliance on data dependence and the last data, shows the component hasn’t been updated. This situation calls for helpuseRefThe reason is that if a REF is bound to data, the ref will not be automatically updated when the data is updated, so that the value of the data before the update can be obtained.

โš ๏ธ Hooks can only be called in functional components and custom Hooks, not in normal functions or class components.

โš ๏ธ can only call Hooks at the first level of the function.

Custom Hooks: You can customize logic that needs to be used repeatedly as Hooks, which must begin with use. I’m not going to do any examples here.

4. Understand components in depth

1, the state

Variables that we use in a component that are not related to rendering should be defined as general properties of the component and should not be placed in state. That is, anything that is not used in render should not be used in state.

Note:

  • State cannot be changed directly as it does not trigger render.
  • Updates to state are asynchronous, and React may merge multiple state changes into one update. Vue has the same mechanism, easy to understand. Updates to the props are also asynchronous.
  • Updating state is a merging process.

React recommends treating state as an immutable object, such as using concat, Slice, filter, etc. to return a new array when there is an array in state, rather than using push, POP, Shift, splice, etc. to modify the array directly. If there are objects, use ES6’s object. assign method or Object extension syntax.

// eg 
this.setState(preState= > ({ arr: preState.arr.slice(1.3)}))// preState is the old state
Copy the code

2. The component communicates with the server

  • Component mount phase communication:componentDidMountHook, officially recommended โœ”๏ธ
componentDidMount(){
    let that = this
    fetch('/getXXX').then(funtion(response){
        that.setState({
          data: response.data
        })
    })
}
Copy the code

The componentWillMount hook is called before the component is mounted and can also fetch data from the server. If rendered on the server, the componentWillMount hook is called twice, and the componentDidMount hook is called only once. So recommend componentDidMount hook.

  • Component update phase communication:componentWillReceiveProps(nextProps)hook

3. Communication between components

  • Parent and child component communication: props

Parent: props property, child parent: callback

// Call methods of the parent component via props
this.props.handleClick(xxx)

// In the parent component
<Child handleClick={this.handleClick}></Child>
Copy the code
  • Sibling communication:State of ascensionStore shared state in the nearest common parent, the core being props
  • Context: When the component hierarchy is too deep, it can be cumbersome to bridge functions. React provides a Context so that any child component at any level can access the states and methods in the parent component. Create context: Add a new context to the component that provides itgetChildContextMethod, which returns the Context object, and then thechildContextTypesProperty defines the type information for the properties of the context object.
// The parent component is Father
getChildContext(){
    return { handleClick: this.handleClick }
}
// ...
Father.childContextTypes = {
    handleClick: PropTypes.func
}

// Child components are accessed through context
this.context.handleClick()
// ...
Child.contextTypes = {
    handleClick: PropTypes.func
}
Copy the code
  • When a project is complex, state management libraries such as Redux can be introduced.

Special ref — gets a DOM element or component

Use ref on DOM:

this.textInput.focus()

/ / in the render
<input type="text" ref={ (input) = > { this.textInput = input }}/>
// input represents the input element
Copy the code

Use ref on components: [class components only]

this.inputInstance.handleChange() // Call the handleChange method of the inputInstance component instance obtained by ref

/ / in the render
<Child ref={ (input) = > { this.inputInstance = input }}/>
// Input represents the component instance
Copy the code

Virtual DOM and performance optimization

1. Virtual DOM — JS objects

There is a principle in front-end performance optimization that minimizes DOM manipulation, and the virtual DOM embodies this principle.

DIFF algorithm: Compared with the old and new virtual DOM, time complexity O(n).

Assumptions:

(1) If two elements have different types, they will generate two different trees.

(2) Set the key attribute for the element in the list, and use the key to identify whether the corresponding element has changed in the render process for many times.

To be specific:

  • When the root node is of a different type, the child node is not compared and the real DOM is generated directly from the new virtual DOM.
  • The root node is the same DOM type, keeping the root node and updating the changed root node attributes;
  • The root node is the same component type, and the corresponding component instance is not destroyed, but only updated;
  • After comparing root nodes, React recursively compares child nodes using the same principle.
  • React provides a key property for lists to reuse.

2. Performance optimization

  • Avoid unnecessary component rendering and use it wiselyshouldComponentUpdateA hook that returns true or false depending on the business logic;
  • Use the key;
  • React Developer Tools for Chrome
  • According to — did you update the plugin

HOC — Abstraction and reuse of component logic

— HighOrderComponent

1. Basic concepts

Higher-order function: a function that takes a function as an argument and returns a function. Similarly, a higher-order component is a component that takes the React component as an argument and returns a new React component.

How is it different from the parent component? Higher-order components emphasize logical abstraction. A higher-order component is a function, which is concerned with logic; A parent component is a component that is primarily concerned with the UI/DOM. If the logic is directly related to the DOM, then that part of the logic should be implemented in the parent component. If the logic is not directly related to the DOM, this part of the logic is suitable for higher-level component abstractions, such as data validation, request sending, and so on.

For example, let’s write a MyComp component to fetch data from localStorage and display:

class MyComp extends React.Component {
  constructor(props) {
    super(props);
  }
  componentWillMount() {
    let data = localStorage.getItem("data");
    this.setState({ data });
  }
  render() {
    return <div>{this.state.data}</div>; }}Copy the code

If other components have this logic, try to reuse it:

// Higher-order components
function HocComp(OtherComp) {
  return class extends React.Component {
    componentWillMount() {
      let data = localStorage.getItem("data");
      this.setState({ data });
    }
    render() {
      return <OtherComp data={this.state.data} {. this.props} / >; }}; }class MyComp extends React.Component {
  render() {
    return <div>{this.props.data}</div>; }}const MycompWithOther = HocComp(MyComp);
Copy the code

You can see the main function of higher-order components: encapsulating and separating the general logic of components so that it can be reused better in components — decorator design patterns [๐Ÿšฉ : See design patterns later].

2. Application scenarios

1) Manipulation props

The previous example

2) Access the component instance through ref
function withRef(WrappedComp) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.someMethod = this.someMethod.bind(this);
    }
    // This saves a reference to the WrappedComp instance
    someMethod() {
      this.instance.methodOfWrappedComp();
    }
    render() {
      return (
        <WrappedComp
          ref={(instance)= >{ this.instance = instance; }} {... this.props} />); }}; }Copy the code
3) Component status is improved

For example, higher-order components are used to uniformly promote the state that the controlled components need to maintain themselves into higher-order components.

4) Wrap components with other elements

Such as adding layouts or changing styles:

function withRedBg(WrappedComp) {
  return class extends React.Component {
    render() {
      return (
        <div style={{ backgroundColor: "red}} ">
          <WrappedComp {. this.props} / >
        </div>); }}; }Copy the code

3. Parameter transfer

Higher-order components can accept other parameters besides components as parameters. Such as:

// Higher-order components
function HocComp(OtherComp, key) {
  return class extends React.Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
      this.setState({ data });
    }
    render() {
      return <OtherComp data={this.state.data} {. this.props} / >; }}; }class MyComp extends React.Component {
  render() {
    return <div>{this.props.data}</div>; }}const MycompWithOther = HocComp(MyComp, "data");
/ / or
const MycompWithOther = HocComp(MyComp, "username");
Copy the code

4, inheritance to achieve higher-order components

Higher-order components implemented by inheritance are often used for render hijacking. For example, allow component rendering while the user is logged in; Otherwise render an empty component:

function withAuth(WrappedComp) {
  return class extends WrappedComp {
    render() {
      if (this.props.isLogin) {
        return super.render();
      } else {
        return null; }}}; }Copy the code

7. Router and Redux for project practice

React Router

The React Router contains three libraries: react-Router, react-router-dom, and react-router-native.

  • The react-router provides basic routing functions. You can install the React-router-DOM (in the browser) or react-router-native (in the react-native) based on the application environment.
  • Both react-router-DOM and react-router-native depend on the React-router, so the react-router is automatically installed during installation.

Install react-router-dom in your Web application:

npm install react-router-dom
Copy the code

React Router Uses the Router and Route components to implement the routing function. A Router can be defined as a Router. Only one Router instance is required in an application. All Route configuration components are defined as sub-components of the Router. 5. Similar to VueRouer

In Web applications, routing is generally divided into:

  • BrowserRouter: H5-based history API,
  • HashRouterBased on the hash:

Route is a component used to configure routing information in the React Router.

import { Route } from 'react-router'
<Route path='/' /> // path matches the path, after which a match object is created as a property in the props to be passed to the rendered component
<Route path='/' component={Home} /> // Render the Home component when matched
// Can also be written as
<Route path='/' render={
  (props) = > {
      <Home {. props} / >
  }
} />
Copy the code

If you only want the first Route to be rendered, you can use Switch:

import {
    BrowserRouter as Router, // Notice here
    Switch,
    Route,
    Link
  } from "react-router-dom";
  
  // For example, home navigation, exact match
  <Router>
    <Switch>
      <Route exact path='/' component={Home} />
      <Route path='/about' component={About} />
      <Route path='/user' component={User} />
    </Switch>
  </Router>
// Without Switch, the /about/user path will match the above three routes and will be rendered
Copy the code

The Link component defines how the page is routed when clicked:

<button><Link to='/login'>The login</Link></button>
// To can also be an object
<Link to={{
  pathname: '/login',
  state: {isLogin : false}
}} />
Copy the code

Redirect components are used for page redirection:

<Redirect to = {xxx}/>
Copy the code

Get routing information in non-routing components: withRouter and Router Hooks.

WithRouter acts a bit like connect in Redux. You pass the component to get routing information to The withRouter. The withRouter passes routing information to the component and returns a new component that can be invoked elsewhere.

import React from "react";
import { withRouter } from "react-router-dom";
function backBtn(props) {
  let { history } = props;
  return (
    <button
      onClick={()= >{ history.goBack(); }} > Return to previous page</button>
  );
}
backBtn = withRouter(backBtn);
export default backBtn;
Copy the code

In addition to using withRouter to get routing information for non-routed components, Router Hooks have been added to Router5.x, using the same rules as other Hooks in React.

1) useHistory: Calling this Hook returns the history object

2) useLocation: Calling this Hook returns the location object

3) useRouteMatch: Calling this Hook returns a match object

4) useParams: Calling this Hook returns params in the match object, which is the argument passed by path

Basic use of Redux – state manager, similar to Vuex

Let’s say we have a data object for a TODO item:

{
    todoList: [{
        text: 'eat'.finished: false}, {text: 'sleep'.finished: false}, {text: 'Beat the beans'.finished: false},].visibilityFilter: 'SHOW_FINISHED'
}
Copy the code

If we need to modify the data, we must send an action:

{ type: 'ADD_TODO'.text: 'movement' }
Copy the code

Type indicates the type of action. This is the field that the action must contain. The rest is undefined. How do you parse the action? Redux uses reducer to resolve actions. Reducer is a normal JS function that receives actions as parameters and returns a new application state.

function todoApp(state = {}, action) {
  switch (action.type) {
    case "ADD_TODO":
    // return new state
    case "XXXX":
    // return new state
    default:
      returnstate; }}Copy the code

These are the basic concepts of Redux: Store [data container], state [data object], Action [modify command], reducer.

Redux applications need to follow three principles:

  • Unique data source: Only one global state object is maintained and stored in Redux’s store
  • Keep the application state read-only: The state cannot be changed directly, but rather based on action
  • State changes are accomplished by pure functions: Action indicates the intention to modify the state, and reducer must be a pure function

PS: A pure function is a function that always has the same output for the same input without side effects, such as modifying external objects or output to I/O devices.

1) Action: distributed through the Store dispatch method
// action creater, return action
function addTodo(text) {
  return {
    type: "ADD_TODO",
    text,
  };
}
Copy the code
Reducer: Describe what happened to the application and respond according to the actions
// reducer
function todoApp(state = {}, action) {
  switch (action.type) {
    case "ADD_TODO":
      return {
        ...state,
        todoList: [
          ...state.todoList,
          {
            text: action.text,
            finished: false,}]};default:
      returnstate; }}Copy the code
3) Store:
  • Save the state
  • Access the state via getState()
  • Send status update actions through Dispatch (Action)
  • Register a listener function with subscribe(listener) to listen for state changes
import { createStore } from 'redux'
function todoApp(){}
const store = createStore(todoApp, initState)

// Get the status
const state = store.getState()
/ / send the action
store.dispatch(addTodo('eat'))
// Register the listener function
let listener = store.subscribe(() = >{
  console.log(store.getState()) // Get the latest status when updating the status
})
// Unsubscribe and call the function returned by store. Subscribe
listener()
Copy the code

Redux data flow process:

(1) Call store.dispatch(Action). An action is an object that describes what happened. Store.dispatch (Action) can be called from anywhere in the application, including components, XHR callbacks, timers.

(2) Call the reducer function for Redux’s store. The store passes two parameters to the Reducer: the status of the current application and the action. The Reducer must be a pure function whose sole responsibility is to calculate the state of the next application.

(3) The root Reducer will combine the return results of multiple sub-reducer into the final application state. The build form of the root Reducer is entirely up to the user. Redux provides combineReducers that make it easy to combine multiple split reducers together, but you can do without it altogether. When combineReducers is used, actions are passed to each reducer, and the results after reducer processing are merged into the final application state.

(4) The store of Redux saves the complete application state returned by the root Reducer. At this point, the application status is updated. If the UI needs to be updated based on application state, then this is the time to update the UI. For React applications, you can call the component’s setState method at this point to update the UI based on the new application state.

Using the story:

npm install react-redux
Copy the code

Components can be divided into container components and presentation components, depending on intent:

  • Container components: Responsible for logic
  • Presentation component: Responsible for the UI

React-redux provides a connect function that connects the React component to redux’s Store to generate a container component responsible for data management and business logic:

import { connect } from "react-redux";
// There is a component TodoList
const VisibleTodoList = connect()(TodoList); // Create a container component
// Pass two parameters to synchronize the container component's state changes
const VisibleTodoList = connect(mapStateToProps, mapDispatchToProps)(TodoList);
// mapStateToProps and mapDispatchToProps are both functions
Copy the code

React-redux provides a Provider component:

// Indicate the code
class Provider extends React.Component {
  getChildContext() {
    return { store: this.props.store };
  }
  render() {
    return this.props.children;
  }
}
Provider.childContextTypes = {
  store: React.PropTypes.object,
};
// Save store to context
Copy the code

The Provider component passes store to the child component through the context, so the Provider component is typically used as the root component:

import { createStore } from "redux";
import { Provider } from "react-redux";

render(
  <Provider>
    <App />
  </Provider>.document.getElementById("root"));Copy the code

React + Redux

1) How to organize the project structure?

  • Files of the same type are organized together according to types: Components, containers, Actions, etc
  • According to page function: a page function corresponds to a folder, including Components, Action, etc
  • Ducks:github.com/erikras/duc… The application state is used as the basis for module division

2) Design State: Design state like a database

  • The state of the entire application is divided into several sub-states according to the domain. Duplicate data cannot be stored between the sub-states
  • State stores data as key-value pairs, indexed by the key or ID of the record, and other fields in the record depend on the index
  • State cannot hold data that can be computed from existing fields in state, that is, fields in state do not depend on each other

Eight, summary

React has higher requirements for JavaScript basics, such as this, higher-order functions, and classes. React also has a higher level of abstraction. Higher-order components, Redux middleware, and so on, each of which is worth digging into.

Anyway, I finally started to learn React. It is true that “frameworks are interlinked”. My experience in using Vue helped me understand React to a large extent. In the future, I plan to write a small demo to increase my proficiency in React and gradually understand the fundamental things. ๐Ÿ”‘