basis

component

The React component can be written in three ways: es6 class syntax. It inherits the React.componentclass to implement components with a full life cycle

import React, { Component } from 'react';

export default class SingleComponent extends Component {
  /* Contains some lifecycle functions, inner functions, and variables */
  render() {
    return (<div>{/ * * /}</div>)}}Copy the code

The second type is stateless components, also known as functional components

const SingleComponent = (props) = > (
  <div>{props.value}</div>
);
export default SingleComponent;
Copy the code

A higher-order component is strictly a higher-order function that wraps the above two components

const HighOrderComponent = (WrappedComponent) = > {
  class Hoc extends Component {
    /* contains some lifecycle functions */
    render() {
      return (<WrappedComponent {. this.props} / >);
    }
  }
  return Hoc;
}
Copy the code

The principle of higher-order components is to accept a component and return a wrapped component. Some life-cycle functions can be inserted into the returned component to perform corresponding operations. Higher-order components can make the logic of the wrapped component be extended from the outside without interference

Props and state

The react component state is called state, which can be initialized using a simple syntax in es6+ class components

export default class Xxx extends Component {
  state = {
    name: 'sakura',
  }
  render() {
    const { name } = this.state;
    return (<div>{name}</div>); }}Copy the code

State can be assigned to a tag. If you need to update state, you can call this.setState() to pass in an object. After changing state with this method, elements bound to the corresponding value will also trigger rendering, which is simple data binding

You cannot modify state by this.state.name = ‘XXX’, as this would lose the effect of updating state and changing the corresponding element

The setState function is an important and frequently used API in React. It takes at most two parameters. The first parameter is the state object to be modified, and the second parameter is a callback function that will be called automatically after the state update operation is completed. Instead of updating state immediately after calling this.setState, React merges objects returned from several consecutive calls to setState to improve performance. The following code may not produce the desired effect

class SomeButton extends Component {
  state = {
    value: 1,
  }
  handleClick = (a)= > {
    const { value } = this.state;
    this.setState({ value: value + 1 });
    this.setState({ value: value + 1 });
    this.setState({ value: value + 1 });
    this.setState({ value: value + 1 });
  }
  render() {
    const { value } = this.state;
    return (<div>
      <span>{vlaue}</span>
      <button onClick={this.handleClick}>click!</button>
    </div>); }}Copy the code

Instead of doing 4 +1 operations on value, React will do a merge of the 4 updates, leaving only one result, something like this

Object.assign({},
  { value: value + 1 },
  { value: value + 1 },
  { value: value + 1});Copy the code

And because setState is asynchronous, you cannot get a new state immediately after the call. If you do, you can only get it by passing the second parameter callback to setState

/* omit some code */
this.setState({
  value: 11,}, () => {const { value } = this.state;
  console.log(value);
})
Copy the code

Props is an attribute object passed by the parent element to the child element, usually used like this

class Parent extends Component {
  /* The parent component holds a value*/ in state
  state = {
    value: 0}; handleIncrease =(a)= > {
    const { value } = this.state;
    this.setState({ value: value + 1 });
  }

  render() {
    const { value } = this.state;
    // Passes props to the Child, and passes a function for the Child to modify value when it clicks
    return (<div>
      <Child value={value} increase={this.handleIncrease} />
    </div>Const Child = (props) => (const Child = (props) => (<div>
    <p>{props.value}</p>
    <button onClick={props.increase}>click!</button>
  </div>
);
Copy the code

The props is like a pipe through which the state of the parent component flows to the child component. This process is called unidirectional data flow

Changing state and props in React causes components to be re-rendered

Component life cycle

The lifecycle is a set of special functions used to represent components from rendering to unloading and to receive new props and state declarations

  • mount
    • componentWillMount
    • render
    • componentDidMount
  • update
    • componentWillReceiveProps
    • shouldComponentUpdate
    • componentWillUpdate
    • render
    • componentDidUpdate
  • uninstall
    • componentWillUnmount

Mount – componentWillMount

At this stage, the component is ready to render the DOM node. You can do some requests and things like that in this method, but because the component has not been rendered for the first time, you cannot get any DOM nodes

Mount – render

This method returns the dom node that needs to be rendered and does the data binding. You cannot call this.setState to change the state in this method, because setState will trigger a re-rendering, causing the render function to be called again to trigger an infinite loop

Mount – componentDidMount

In this phase, the component is rendered for the first time, and you can retrieve the actual DOM node, do some request operations in this method, or bind events, etc

Update – componentWillReceiveProps

This method is automatically triggered when the component receives new props and state and has not executed render. In this phase, the new props and state can be obtained. In some cases, some operations may need to be performed based on the comparison between the old props and the new props. For example, the popover state of a popover component is stored in the parent component’s state and passed to the props itself

class Dialog extends Component {
  componentWillReveiceProps(nextProps) {
    const { dialogOpen } = this.props;
    if(nextProps.dialogOpen && nextProps.dialogOpen ! == dialogOpen) {/* Pop-ups pop up */}}}Copy the code

Update – shouldComponentUpdate

ShouldComponentUpdate is a very important API. React the components of the update process through above several stages, arrived at this stage need to confirm whether a component rendering again really need according to the new state, confirmed on the basis of whether is the contrast between old and new state change, if there is no change, return false function does not perform at the back of the life cycle, if change the returns true, Continue with the rest of the life cycle, and React returns true by default

So shouldComponentUpdate can be used to optimize performance, you can manually implement shouldComponentUpdate function to compare the state of the difference before and after, so as to prevent unnecessary repeated rendering components

class Demo extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    return this.props.value !== nextProps.value;
  }
}
Copy the code

Value of this.props. Value and nextProps. Value are the same to determine whether the component needs to be re-rendered. The deeper the data nesting is, the deeper the comparison is, the more expensive the react performance is, the more Immutable

Because imMutableJS itself is immutable, it returns a new object if it needs to modify the state, and because it returns a new object after modification, shouldComponentUpdate method just need to compare the object reference is easy to get the result, do not need to do a deep comparison. But using imMutableJS means increasing learning costs, so there are trade-offs to be made

Update – componentWillUpdate

ShouldComponentUpdate this phase is called automatically when the new state is received and shouldComponentUpdate is sure that the component needs to be rerendered but has not yet been rendered. This phase still gets the new props and state and is the last chance to update the state before the component is rerendered

Update – render

Rerender according to the new state

Update – componentDidMount

Re-render

Uninstall – componentWillmount

This is where timers can be cleared and events can be dismissed before components are unloaded

Component communication

In many service scenarios, communication between parent components, parent components, and even sibling components is often involved. Parent components can communicate with each other through props. Child => parent component communication is a common problem for most beginners. Suppose you have a requirement, a child component is a drop-down selection menu, a parent component is a form, and after selecting an item in the menu, you need to pass the value to the parent form component. This is a typical requirement for the child => parent component to pass the value

const list = [ { name: 'sakura', id: 'x0001' }, { name: 'misaka', id: 'x0003' }, { name: 'mikoto', id: 'x0005' }, { name: 'react', id: 'x0002' }, ]; class DropMenu extends Component { handleClick = (id) => { this.props.handleSelect(id); } render() { <MenuWrap> {list.map((v) => ( <Menu key={v.name} onClick={() => this.handleClick(v.id)}>{v.name}</Menu> ))}  </MenuWrap> } } class FormLayout extends Component { state = { selected: '', } handleMenuSelected = (id) => { this.setState({ selected: id }); } render() { <div> <MenuWrap handleSelect={this.handleMenuSelected} /> </div> } }Copy the code

In this example, the parent component, FormLayout, passes a function to the child component. The child component’s Menu click calls this function and passes in the value. The parent component receives the value

However, for the more complex components of the same level or even similar to the relationship between uncle and nephew, they can communicate with each other through the way of state promotion. Simply put, if the two components are not nested and there is no parent-child relationship, in this case, they can find the common parent component of their upper layer and store the state in this parent component. Pass the corresponding state and the corresponding callback functions to the two components via props

routing

The most common routing solution in React is the React-router. The react-router has gone through four iterations with major API changes in each version. In this article, we will introduce the latest version of React-router-V4

Basic usage

To use routing, you need to wrap the App with the Router component and pass the history object through props. In the latest version, history is separated into a package and needs to be imported before using it. For the Switch of peer component routes, multiple routes need to be wrapped with the Switch component. Each time the Route changes, only one matching component will be rendered

import ReactDOM from 'react-dom';
import createHistory from 'history/createBrowserHistory';
import { Router } from 'react-router';

import App from './App';

const history = createHistory();

ReactDOM.render(
  <Router history={history}>
    <App />
  </Router>,
  element,
);

// App.js
/ /... Omit some code

import {
  Switch, Route,
} from 'react-router';

class App extends Component {
  render() {
    return( <div> <Switch> <Route exact path="/" component={Dashboard} /> <Route path="/about" component={About} /> </Switch> </div> ); }}Copy the code

CodesanBox online example

State management

For single-page application state management, you can first read this article about single-page application data flow solution exploration

The React ecosystem’s state management solution is based on The Flux architecture proposed by Facebook and has several different implementations. The two most popular are

  • Mobx
  • Redux

Flux

Flux is the application architecture that Facebook uses for building client-side web applications. It complements React’s composable view components by utilizing a unidirectional data flow. It’s more of a pattern rather than a formal framework, and you can start using Flux immediately without a lot of new code.

Flux is a facebook architecture for building Web applications that complements React components by using a one-way data flow supplement. It is a pattern, not a formal framework

First, Flux divides an application into three parts:

  • dispatcher
  • stores
  • views

dispatcher

The Dispatcher is the central hub for managing all data flows in the Flux application. Its function is simply to distribute actions to stores. Each store listens to itself and provides a callback function

Facebook’s official implementation of dispatcher.js

stores

Stores contain the state and logic of the application, similar to the Model in traditional MVC, stores are used to store the state of a specific area scope in the application

A store registers an event with the Dispatcher and provides a callback function that takes action as an argument and distinguishes and interprets actions based on actionType. The corresponding data update function is provided in the Store, and after confirming the update, an event is broadcast for the application to update the view based on the new state

// Facebook officially implements FluxReduceStore usage
import { ReduceStore, Dispatcher } from 'flux';
import Immutable from 'immutable';
const dispatch = new Dispatcher();

class TodoStore extends ReduceStore {
  constructor() {
    super(dispatch);
  }
  getInitialState() {
    return Immutable.OrderedMap();
  }
  reduce(state, action) {
    switch(action.type) {
      case 'ADD_TODO':
        return state.set({
          id: 1000.text: action.text,
          complete: false});default:
        returnstate; }}}export default new TodoStore();

Copy the code

views

React provides views that can be combined and freely rerendered. In the React topmost component, it uses some glue code to get the required data from stores and pass the data to its sub-components via props. We can manage the state of any part of the page by controlling the state of the top-level component

The official Facebook implementation has a fluxContainer.js that connects the Store and React components and refreshes the component status update view after the Store updates data. The basic idea is to pass in the Stores and the state and methods required by the component, as well as the component itself, with a higher-order component, and return the component that has injected the state and action methods. The basic usage looks like this

import TodoStore from './TodoStore';
import Container from 'flux';
import TodoActions from './TodoActions';

// There can be multiple stores
const getStores = (a)= > [TodoStore];

const getState = (a)= > ({
  / / state
  todos: TodoStore.getState(),

  // action
  onAdd: TodoActions.addTodo,
});

export default Container.createFunctional(App, getStore, getState);
Copy the code

The CodeSanbox online sample will be supplemented later with the source code parsing of flux’s official implementation

Redux

Redux is another implementation of Flux architecture by Dan Abramov. It continues the ideas of views, Store and dispatch in Flux architecture and makes improvement on this basis by splitting the reduce function in the original store into reducer. And merge multiple stores into one store to make it more convenient for testing

The Evolution of Flux Frameworks

The first change is to have the action creators return the dispatched action.What looked like this:


export function addTodo(text) {
  AppDispatcher.dispatch({
    type: ActionTypes.ADD_TODO,
    text: text
  });
}
Copy the code

can look like this instead:

export function addTodo(text) {
  return {
    type: ActionTypes.ADD_TODO,
    text: text
  };
}
Copy the code

Stores were split into a single store and multiple reducer

const initialState = { todos: []};export default function TodoStore(state = initialState, action) {
  switch (action.type) {
  case ActionTypes.ADD_TODO:
    return { todos: state.todos.concat([action.text]) };
  default:
    return state;
}
Copy the code

Redux divides the application into four parts

  • views
  • action
  • reducer
  • store

Views can trigger an action, and the Reducer function internally performs operations on the data according to different actions. Finally, a new state is returned, and the Store forms a state tree from all the states returned by the Reducer. Updates to views via subscribed event functions

views

The React component acts as the view layer in the application

action

Action is a simple JavaScript object that contains a Type attribute and the parameters required for the action action. It is recommended to use actionCreator to return an action, which can be passed to the component as state

function singleActionCreator(payload) {
  return {
    type: 'SINGLE_ACTION',
    paylaod,
  };
}
Copy the code

reducer

Reducer is a pure function that simply returns corresponding output according to the specified input. The Reducer function should have no side effects and finally needs to return a state object. For multiple reducer functions, combineReducer functions can be combined

function singleReducer(state = initialState, action) {
  switch(action.type) {
    case 'SINGLE_ACTION':
      return { ...state, value: action.paylaod };
    default:
      returnstate; }}function otherReducer(state = initialState, action) {
  switch(action.type) {
    case 'OTHER_ACTION':
      return { ...state, data: action.data };
    default:
      returnstate; }}const rootReducer = combineReducer([
  singleReducer,
  otherReducer,
]);

Copy the code

store

There is only one store in redux. A store can be created by calling createStore and passing it into reducer. This store contains several methods, namely subscribe, Dispatch, getState, and replaceReducer. Subscribe registers a callback function for state updates, dispatch manually triggers an action, getState gets the current state tree, replaceReducer replaces reducer, redux is used in react projects, You have to combine it with React-redux

import { connect } from 'react-redux';
const store = createStore(rootReducer);

// App.js
class App extends Component {
  render() {
    return (
      <div>
        test
      </div>); }}const mapStateToProps = (state) = > ({
  vlaue: state.value,
  data: state.data,
});

const mapDispatchToProps = (dispatch) = > ({
  singleAction: (a)= > dispatch(singleActionCreator());
});

export default connect(mapStateToProps, mapDispatchToProps)(App);

// index.js
import { Provider } from 'react-redux';

ReactDOM.render(
  <Provider store={store}>
    <APP />
  </Provider>,
  element,
);
Copy the code

CodeSanbox online example

Because my technical level is limited, if there is a mistake in the article or careless mistake welcome big brothers to point out

Blog address, update periodically