Senior front-end software engineer knowledge collation of React technology stack articles

1. Introduce JSX?

JSX is a syntax extension of JavaScript that is used in the React architecture to describe user interfaces. In this grammatical environment, curly braces are used to represent JSX expressions, and the expressions can be declared variables and functions. When referencing a style, the attribute name class should be changed to className, such as:

var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));

2. What is virtual DOM?

Virtual DOM (VDOM) can be understood as an object that uses JavaScript to describe the real DOM tree structure, which includes all the information of the entire DOM structure. The birth of virtual DOM stems from the need for interface rendering optimization. To render a page, the browser can roughly be divided into five steps:

  1. Use an HTML analyzer to analyze HTML elements and build a DOM tree.
  2. Use a CSS analyzer to analyze CSS files and inline styles on elements to generate a style sheet for the page.
  3. Associate the above DOM tree with the style sheet to build a Render tree. This process is also called Attachment. Each DOM node has an attach method that accepts style information and returns a render object, and these render objects will eventually be built into a Render tree.
  4. After having the Render tree, the browser starts layout, and will determine a precise coordinate value that appears on the display screen for each node on the Render tree.
  5. With the Render tree, the position coordinates displayed by the nodes are also available. Finally, the paint method of each node is called to display them.

Unfortunately, the browser is not very smart when rendering. For example, to update the information of 10 DOM nodes, the ideal state is to build the DOM tree at one time and then perform rendering, but the browser will repeat this process 10 times. Such frequent manipulation of the DOM is expensive.

Virtual DOM is designed to solve this browser performance problem. If there are 10 actions to update DOM in one operation, virtual DOM will not operate DOM immediately, but save the diff content of these 10 updates to the local In a js object, the js object is finally built into the DOM tree at one time, and the browser is notified to perform rendering, which can avoid a large amount of unnecessary calculations.

3. How to pass values ​​between React parent and child components?

Passing parameters between parent and child components can use the props attribute, such as:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Child extends Component {
  static propTypes = {
    name: PropTypes.string,
    clickHandle: PropTypes.func
  }

  render() {
    const { name, clickHandle } = this.props;
    return (
      <div onClick={clickHandle}>{name}</div>
    );
  }
}

class App extends Component {
  state = {
    name: 'App'
  }

  clickHandle = () => {
    console.log('click!!!');
  }

  render() {
    return (
      <Child name={this.state.name} clickHandle={this.clickHandle} />
    );
  }
}

export default App;

4. An array of objects, each sub-object contains an id and name, how does React render the list of names?

In the React technology stack, when an array containing elements is directly placed in a JSX expression, the array will be directly expanded and the elements in the array will be rendered to the interface. like:

class App extends React.Component{
    render(){
        return (
            <ul>
                {[
                    <li>1</li>,
                    <li>2</li>,
                    <li>3</li>
                ]}
            </ul>
        )
    }
}

// 渲染结果
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

Therefore, when we get an array object [obj1, obj2], we can use an Array.proptypes.map() function to render the data. Note that the key attribute should be added to each Item as a unique identifier. like:

import React, { Component } from 'react';

class App extends Component {

  state = {
    list: [
      {
        id: '1',
        name: 'one'
      },
      {
        id: '2',
        name: 'two'
      }
    ]
  }

  renderList = (list) => {
    let listDom = null;
    listDom = (
      <ul>
        {list.map((item)=>{
          return this.renderItem(item);
        })}
      </ul>
    );
    return listDom;
  }

  renderItem = (item) => {
    return <li key={item.id}>{item.name}</li>;
  }

  render() {
    return (
      <div>
        {this.renderList(this.state.list)}
      </div>
    );
  }
}

export default App;

5. There is an input and a p tag on the page. After changing the input, the p tag will change accordingly. How to deal with it? Which event to listen to input?

This interview question tests the understanding of two-way binding. The main purpose is to bind the value of input and the value of p in the same state. By listening to the onChange event of input, when the input value changes, change the bound state value, so that the value of p will also change. The implementation code is as follows:

import React, { Component } from 'react';

class App extends Component {

  state = {
    value: ''
  }

  changeHandle = (e) => {
    this.setState({value:e.target.value});
  }

  render() {
    return (
      <div>
        <input value={this.state.value} onChange={this.changeHandle} />
        <p>{this.state.value}</p>        
      </div>
    );
  }
}

export default App;

6. What hook functions does React life have, and how to understand them? What is the trigger condition of componentWillReceiveProps? What problem shouldComponentUpdate solves?

There are 7 React life cycle hook functions:

hook function describe
componentWillMount  Before the component is mounted: Called before rendering, applicable to both the client and the server.
componentDidMount After the component is mounted: Called after the first rendering is completed, applicable to the client, after which the component has generated the corresponding DOM structure, which can be accessed through this.getDOMNode().
componentWillReceiveProps Component receives a new parameter: Triggered when the component receives a new prop, this hook function will not be called when the component is initialized.
shouldComponentUpdate Whether to update the component: returns a boolean value, which is triggered when the component receives new props or state. This hook function will not be called when initializing or using forceUpdate. When false is returned, the component will not be updated.
componentWillUpdate Before the component is updated: it is called when the component receives new props or state but has not been rendered yet. This hook function will not be called during initialization.
componentDidUpdate After component update: Called immediately after the component has finished updating. Will not be called during initialization.
componentWillUnmount Component Removed: Called immediately when the component is removed from the DOM.

The trigger condition of componentWillReceiveProps is: trigger when the props parameter passed in by the parent component changes. This hook function is often used to realize the effect of changing the input parameter of the parent component to drive the interface change of the child component.

shouldComponentUpdate mainly determines whether the component needs to be updated by judging the props parameter. When it returns false, it means that the component will not be updated.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Content extends Component {

  static propTypes = {
    num: PropTypes.number
  }

  state = {
    name: 'Content'
  }

  UNSAFE_componentWillMount(){
    console.log('componentWillMount');
  }

  componentDidMount(){
    console.log('componentDidMount');
  }

  UNSAFE_componentWillReceiveProps(newProps){
    console.log('componentWillReceiveProps', newProps);
  }

  shouldComponentUpdate(newProps, newState){
    console.log('shouldComponentUpdate', newProps, newState);
    return true; 
  }

  UNSAFE_componentWillUpdate(nextProps, currentState){
    console.log('componentWillUpdate', nextProps, currentState);
  }

  componentDidUpdate(prevProps, prevState){
    console.log('componentDidUpdate', prevProps, prevState);
  }

  componentWillUnmount(){
    console.log('componentWillUnmount');
  }

  render(){
    return (<p>{this.props.num}</p>);
  }
}

class App extends Component {

  state = {
    num: 0
  }

  setNum = () => {
    this.setState({num:this.state.num+1});
  }

  render() {
    return (
      <div>
        <button onClick={this.setNum}>addOne</button>
        <Content num={this.state.num}></Content>
      </div>
    );
  }
}

export default App;

// 点击前
// componentWillMount
// componentDidMount
// 点击后
// componentWillReceiveProps {num: 1}
// {num: 1} {name: "Content"}
// {num: 1} {name: "Content"}
// {num: 0} {name: "Content"}

7. How to configure react-router? How does react-router implement routing switching? What is the difference between the <Link> tag and the <a> tag in react-router? What did you do to achieve the jump after the default event of the <a> tag was disabled?

The main usage scenario of React Router is Single Page Rich Application (SPA). There are two implementations, one is to use the hash of the url, which is often referred to as the anchor point (#), JavaScript monitors the change of the url through the hashChange event; the other is the History mode of HTML5, which makes the url look like a normal website In that way, separated by "/", without #, but this mode requires server support, otherwise 404 will be reported when refreshing.

There are two ways to introduce React Router, react-router and react-router-dom. Most of the time, we use the react-router-dom package because it includes some DOM components for routing implementation, such as NavLink. The react-router-dom initialization statement is as follows:

$ npm install react-router-dom -S

After the installation is complete, the routing can be built. The simplest routing structure is as follows:

import {Route, NavLink, Switch, HashRouter } from 'react-router-dom'

render() {
	return(
		<HashRouter basename="/">
			<div>
				<div>
					<ul>
						<li><NavLink exact to="/" activeClassName="selected">打开First页面</NavLink></li>
						<li><NavLink exact to='/second' activeClassName="selected">打开Second页面</NavLink></li>
						<li><NavLink exact to='/third' activeClassName="selected">打开Third页面</NavLink></li>
					</ul>
				</div>
				<div>
					<Switch>
						<Route exact path="/" component={First}></Route>
						<Route exact path="/second" component={Second}></Route>
						<Route exact path="/third" component={Third}></Route>
						<Route component={NoMatch}/>
					</Switch>
				</div>
			</div>
		</HashRouter>
	);
}

The <NavLink> tag indicates an accessible navigation link, and exact indicates an exact match, that is, the response interface will only be opened when the to value is consistent with the path value in the <Route> tag. activeClassName represents the style when the link is active. The <Switch> tag indicates that as long as the match is found, it will stop continuing to match, and ensure that only one page is displayed each time the navigation link is opened. The component attribute indicates the routing page displayed when the match is correct. The last <Route> has no path attribute, which means that when there is no match, the NoMatch page will be displayed.

When opening a navigation link, there are two ways to pass parameters, both of which are set in the to attribute:

<ul>
	<li><NavLink exact to="/" activeClassName="selected">打开First页面</NavLink></li>
	<li><NavLink to='/second/002' activeClassName="selected">打开Second页面</NavLink></li>
	<li><NavLink to={
   
   {
		  pathname: '/third',
		  search: '?search=userinfo',
		  state: { id: '003' }
		}} activeClassName="selected">打开Third页面</NavLink></li>
</ul>
<Switch>
	<Route exact path="/" component={First}></Route>
	<Route path="/second/:id" component={Second}></Route>
	<Route path="/third" component={Third}></Route>
</Switch>
  • Directly add character parameters after the to path, such as second in the above example.
  • to points to an object, and parameters are set in the object, such as third in the above example.

The page needs to obtain the parameters passed in through the this.props.match object.

If you need to implement redirection, you can use Redirect, the usage is as follows:

<Route exact path="/second" render={props => (
	<Redirect
        to={
   
   {
          pathname: "/third",
          state: { from: props.location }
        }}
    />)}>
</Route>

This code means that when the second link is clicked, the third page will be opened.

The difference between the <Link> tag and the <a> tag in react-router is that the page jump through the <a> tag will re-render the entire page, while the <Link> tag will only render the changed part, that is, the routing rendering component section, more economical performance.

The <a> tag realizes the jump:

The <Link> tag realizes the jump:

After the default event of the <a> tag is disabled, if you want to jump to the page, you can add a click event to jump through the js script logic.

For more detailed configuration and usage of routing, you can refer to my other article "React Router 4 Usage Examples and API Details"

8. How to achieve dynamic loading of routes?

Route dynamic loading, also known as route lazy loading. It means that when packaging, the pages are divided into multiple chunk modules according to the route imported from the navigation page and loaded on demand, so as to avoid packaging them into the navigation page, which will cause the file to be too large and slow to load. When clicking on a route navigation <Link>, load the js file of the page. To implement dynamic loading of routes, you can use the react-loadable library.

cnpm install --save react-loadable

Example:

import Loadable from 'react-loadable';
import Loading from './my-loading-component';

const LoadableComponent = Loadable({
  loader: () => import('./my-component'),
  loading: Loading,
});

export default class App extends React.Component {
  render() {
    return <LoadableComponent/>;
  }
}

For more detailed methods, please refer to

https://www.jianshu.com/p/462bb9d1c982

http://www.cnblogs.com/zhuzeliang/p/9110400.html

9. What is the difference between routing history and hash?

  • The path shown is different. The path displayed by the hash has an anchor point (#), while the history looks like most ordinary website addresses, separated by "/" without #.
  • The result of performing a refresh operation is different. Hash can refresh the page at will, but a 404 error will be reported when the history is refreshed, because there is no such resource in the server, you need to configure the apache or nginx url to redirect to the index homepage.
  • Browser compatibility varies. Hash supports low-version browsers and IE browsers. The browser used by history must support HTML5's new API: pushState, replaceState(可以实现replace the url without refreshing the page)。

Regarding which route to choose: In addition to considering the compatibility of the displayed url and the browser, some problems that may be caused by the # sign must also be considered, such as app sharing. In some apps, the url is not allowed to have the # sign, so you must If you remove the # sign, you need to use the history mode. Everything else doesn't matter much.

10. Introduce Redux, what problem does it mainly solve? What is the data flow like? How is multiple components using the same state managed?

Redux is an MVC design idea, or an MVC mechanism.

Mainly solve the problem:

In the React technology stack, Redux mainly solves the problem of state sharing between components and realizes multi-component communication.

Redux data flow:

Redux mainly includes 4 modules, and the general data flow is as follows:

  • view - user interface, when a user operates the component to change the state of the component, an action is triggered.
  • action - Generate an action object describing the state change and pass it to the store.
  • store - receives the action object and passes it to the reducer, drives the reducer to perform the state update task, and automatically refreshes the dom node after the state update, that is, executes the render() function in the view. 
  • reducer - process the action object, update the component state, and return the new state value to the store.

For more information on the data flow of Redux and the interpretation of the source code of the react-redux library, you can refer to my other article "Redux Interpretation of the Development and Evolution of Flux, Redux to react-redux"

Multiple components use the same state management:

Multiple components use the same state, they only need to be managed by the same reducer.

11. How is the state injected into the component, and what is the process from the reducer to the component?

Inject the state into the component through connect and mapStateToProps, example:

import { connect } from 'react-redux'
import { setVisibilityFilter } from '@/reducers/Todo/actions'
import Link from '@/containers/Todo/components/Link'

const mapStateToProps = (state, ownProps) => ({
	active: ownProps.filter === state.visibilityFilter
})

const mapDispatchToProps = (dispatch, ownProps) => ({
	setFilter: () => {
		dispatch(setVisibilityFilter(ownProps.filter))
	}
})

export default connect(
	mapStateToProps,
	mapDispatchToProps
)(Link)

In the example, active is the state injected into the Link component. There are two parameters in mapStateToProps(state, ownProps), meaning:

  • state - the global state object managed by the store, all component state data is stored in this object.
  • ownProps - The parameters passed in by the component through props.

From the reducer to the component, the state goes through the following process:

(1) The reducer processes the action object, updates the component state, and returns the new state value to the store.

(2) Upgrade the component Component through connect(mapStateToProps, mapDispatchToProps)(Component). At this time, the state value is taken out from the store and passed to the component as a props parameter.

The main implementation source code of the high-level component connect is as follows:

import React from 'react'
import PropTypes from 'prop-types'
 
// 高阶组件 contect 
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
	class Connect extends React.Component {
		// 通过对context调用获取store
		static contextTypes = {
			store: PropTypes.object
		}
 
		constructor() {
			super()
			this.state = {
				allProps: {}
			}
		}
		
		// 第一遍需初始化所有组件初始状态
		componentWillMount() {
			const store = this.context.store
			this._updateProps()
			store.subscribe(() => this._updateProps()); // 加入_updateProps()至store里的监听事件列表
		}
		
		// 执行action后更新props,使组件可以更新至最新状态(类似于setState)
		_updateProps() {
			const store = this.context.store;
			let stateProps = mapStateToProps ?
				mapStateToProps(store.getState(), this.props) : {} // 防止 mapStateToProps 没有传入
			let dispatchProps = mapDispatchToProps ?
				mapDispatchToProps(store.dispatch, this.props) : {
                                    dispatch: store.dispatch
                                } // 防止 mapDispatchToProps 没有传入
			this.setState({
				allProps: {
					...stateProps,
					...dispatchProps,
					...this.props
				}
			})
		}
 
		render() {
			return <WrappedComponent {...this.state.allProps} />
		}
	}
	return Connect
}

12. How to split the state between multiple components, each small component has its own state, and there are some common states between them that need to be maintained, how to think about this?

This question can be understood as, should all the state of the application be handed over to redux for processing? The answer is NO.

Redux should only be responsible for managing common state, such as user information, login information, and some data-level states, such as time, weather, shopping cart, product list, etc.

For some UI-level state changes, it should be placed in the private state of the component. These categories include:

  • The toggle state at the UI level, such as expand all, load more, etc.
  • State values ​​maintained for animation, such as dragging to change component coordinates.
  • The state of the input in the form.

Back to this question, each small component has its own state, then put this part of the state in the private state of the component; their public state will be placed in Redux.

13. What is Redux middleware, how many parameters does it accept? How does the middleware get the store and action, and then how to deal with them? What are the used Redux middleware, and briefly introduce them?

Redux middleware refers to doing something between the process after dispatch(action) is triggered but before reducer(state, action) . If you don’t understand this sentence, you can understand it by looking at the source code of createStore. Part of the source code of createStore :

export const createStore = (reducer) => {
	let state = null
	const listeners = []; // 事件监听列表
	const subscribe = (listener) => listeners.push(listener); // 定义添加事件对外接口
	const getState = () => state; // 定义获取状态总值对外接口
	// 定义驱动 Aciton 的对外接口,每次驱动会遍历执行listeners列表里的所有事件
	const dispatch = (action) => {
		state = reducer(state, action)
		listeners.forEach((listener) => listener())
	}
	dispatch({}); // 首次初始化state
	return {
		getState,
		dispatch,
		subscribe
	}
}

In other words, if no middleware is added, executing the dispatch(action) function is actually executing reducer(state, action).

There are two main parameters received by middleware: store and action.

So how does the middleware get the two parameters of store and action? Let's first look at a script that uses the middleware redux-thunk:

import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import reducer from './reducers'
import App from './containers/App'
 
const middleware = [thunk];
const store = createStore(reducer, applyMiddleware(...middleware))
 
render(
	<Provider store={store}>
    	    <App />
  	</Provider>, document.getElementById('root')
)

It can be seen that the middleware associates redux-thunk with the store through the applyMiddleware function, that is to say, the store is passed into the middleware when the store is initialized . Now, let's take a look at the source code of a middleware redux-thunk for asynchronously obtaining state values:


function createThunkMiddleware(extraArgument) {
  return function (_ref) {
    var dispatch = _ref.dispatch,
        getState = _ref.getState;
    return function (next) {
      return function (action) {
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }
 
        return next(action);
      };
    };
  };
}
 
var thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

According to the source code analysis, the action is passed into the middleware after dispatch(action) is triggered for the first time. After dispatch(action) is triggered for the first time, due to the existence of middleware, Redux does not immediately execute next(action), that is, reducer(state, action), but first judges the action type, and if it is a function type, then interrupt execution next(action), and hand over the control of dispatch to action, that is to say, when to execute reducer(state, action) you can decide for yourself. This is the key to the middleware redux-thunk to achieve asynchrony, to get the control of dispatch.

In addition, if multiple middleware are applied at the same time, the parameter action of the middleware is updated iteratively. like:

const middleware = [A, B, C];
const store = createStore(reducer, applyMiddleware(...middleware))

At this point, the process of the action value obtained by the middleware is:

  • The action obtained by middleware A is the action value of dispatch(action) for the first time. After being processed by middleware A, dispatch(actionA) is triggered
  • Middleware B gets the action value processed by middleware A, namely actionA, and after being processed by middleware B, dispatch(actionB) is triggered
  • In the same way, middleware C gets the action value processed by middleware B, namely actionB, and after being processed by middleware C, dispatch(actionC) is triggered
  • The final execution result is: reducer(state, actionC)

If you want to customize middleware, you can use the following function definition:

const myMiddleware = (store) => (next) => (action) => {
	// 对action数据进行操作
	// 返回action对象
	next(action)
}
const middleware = [myMiddleware];
const store = createStore(reducer, applyMiddleware(...middleware))

Finally, what middleware have you used? This problem varies from person to person. For example, the middleware redux-thunk gets control of Dispatch and is used to realize the function of asynchronously obtaining status values.

14. How does Redux request middleware handle concurrency?

First, you need to use the redux-thunk middleware to obtain the control of Dispatch, so that you can freely control how to trigger the reducer to change the state value. Then you only need to implement concurrent requests. Here are two recommended methods:

  • Use the Promise.all() function to achieve concurrency.
  • Use async, await and Array.map() to achieve concurrency. Note that using a for or for...of loop triggers sequential requests rather than concurrent ones.

Concurrent code refers to the last point of "Advanced Front-end Interview Finishing: Asynchronous Chapter" .

15. Introduce the principle of React component event proxy and what problems are mainly solved?

React has built a set of event binding mechanisms that are different from JavaScript natives. This mechanism fully complies with W3C specifications and supports all functions of native events. In addition, it also implements event proxy functions. Event proxy means that no matter how many events of the same type are bound, they are only registered once, and a 1-to-N mapping relationship will be established between them . When an event is triggered, the N event functions that execute the mapping will be traversed. Events bound under React's event mechanism are called synthetic events (SyntheticEvent).

The difference between React's event binding mechanism and native ones is that the range of binding objects, e.stopPropagation in the callback function to prevent bubbling, and the event removal mechanism are different .

(1) Native

  • Binding object - element binding, such as div etc.
  • The scope of e.stopPropagation - e.stopPropagation can reach the document layer when bubbling is prevented
  • Event removal - When deleting an element, you need to use removeEventListener to remove the corresponding event, otherwise it will cause a memory leak

(2) react

  • Binding object - document binding
  • The scope of e.stopPropagation -  use e.stopPropagation to prevent bubbling from reaching the document layer
  • Event removal - when an element is deleted, the corresponding event is automatically removed

Let's look at an example:

import React, { Component } from 'react';

class App extends Component {

  componentDidMount() {
    document.getElementById('nativeDom').addEventListener('click', this.nativeDomClick);
    document.addEventListener('click',()=>{
      alert('document');
    });
  }

  componentWillUnmount() {
    document.getElementById('nativeDom').removeEventListener('click');
    document.removeEventListener('click');
  }
  
  reactDomClick = (e) => {
    alert('react');
    e.stopPropagation(); // 冒泡失败,无法阻止document触发click事件

    // 打印:
    // native
    // document
  }

  nativeDomClick = (e) => {
    alert('native');
    e.stopPropagation(); // 冒泡成功,阻止了document触发click事件

    // 打印:
    // native
  }

  render() {
    return (
      <div>
        <div id='reactDom' style={styles.reactDomStyle} onClick={this.reactDomClick}></div>
        <div id='nativeDom' style={styles.nativeDomStyle}></div>
      </div>
    );
  }
}

const styles = {
  reactDomStyle: {
    width: '100px',
    height: '100px',
    backgroundColor: 'red'
  },
  nativeDomStyle: {
    width: '100px',
    height: '100px',
    backgroundColor: 'yellow'
  }
};

export default App;

It can be proved by examples that using e.stopPropagation to prevent bubbling in react cannot reach the document layer (others are no different from the original, that is to say, the parent container other than the document layer is still blocked from bubbling as the original) .

So how to solve this problem? You can use e.nativeEvent.stopImmediatePropagation()

reactDomClick = (e) => {
  alert('react');
  e.nativeEvent.stopImmediatePropagation(); // 冒泡成功,阻止了document触发click事件

  // 打印:
  // native
}

React's event proxy mainly solves the problem of avoiding registration of multiple events of the same type and increasing performance consumption. So you can safely execute the following code in react:

this.state.list.map(
  (item) => {
    return (<li key={item.key} onClick={()=>{this.clickHandle(item.name);}}>{item.name}</li>);
  }
)

More references: https://github.com/youngwind/blog/issues/107

16. Is setState synchronous or asynchronous? So in what scenario is it asynchronous, can it be synchronous, and in what scenario is it synchronous?

setState can be asynchronous or synchronous, the main determinant depends on the environment in which setState is triggered.

Asynchronous scenario:

  • Trigger setState on a synthetic event. Such as onClick and so on.
  • Trigger setState in the lifecycle hook function.

Example:

import React, { Component } from 'react';

class App extends Component {

  state = {
    a: 0,
    b: 0
  }

  clickHandle = () => {
      this.setState({a: this.state.a + 1, b: 1});
      this.setState({a: this.state.a + 2});
      this.setState({a: this.state.a + 3});
      console.log('a:',this.state.a); // 0
      console.log('b:',this.state.b); // 0
  }

  render() {
    console.log('ra:',this.state.a); // 3
    console.log('rb:',this.state.b); // 1
    return (
      <div onClick={this.clickHandle}>
        click
      </div>
    );
  }
}

export default App;

In the composition function environment, setState is executed asynchronously, so the values ​​of a and b printed in the clickHandle function are both 0. In addition, after the rendering is completed, the printed value of ra is 3, and the value of rb is 1. However, the value of a has been added 3 times, why not 1+2+3=6?

This is because in the case of asynchronous execution, no matter how many times setState is executed, if it contains the same state attribute, the latter will overwrite the previous setting, so the final override result is: setState({a:this.state.a + 3, b:1}), which is actually only rendered once.

Synchronization scenario:

  • Trigger setState in setTimeout function
  • Trigger setState in native events, such as document.addEventListener('click', function(){...})

Example:

import React, { Component } from 'react';

class App extends Component {

  state = {
    a: 0,
    b: 0
  }

  clickHandle = () => {
    setTimeout(()=>{
      this.setState({a: this.state.a + 1, b: 1});
      this.setState({a: this.state.a + 2});
      this.setState({a: this.state.a + 3});
      console.log('a:',this.state.a); // 6
      console.log('b:',this.state.b); // 1
    },0);
  }

  render() {
    // 最后一次渲染
    console.log('ra:',this.state.a); // 6
    console.log('rb:',this.state.b); // 1
    return (
      <div onClick={this.clickHandle}>
        click
      </div>
    );
  }
}

export default App;

// 点击后打印结果:
// ra: 1
// rb: 1
// ra: 3
// rb: 1
// ra: 6
// rb: 1
// a: 6
// b: 1

The example proves that in a synchronous environment, setState will be executed several times synchronously, and the next setState will not enter until the render function is executed.

17. What is the difference between pureComponent and Component?

React.PureComponent is almost identical to React.Component, but React.PureComponent implements shouldComponentUpate() through a shallow comparison of props and state. The source code of shallow comparison judgment is as follows:

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}

where shallowEqual is a function in react. However, using pureComponent also has certain risks. For example, if it contains more complex data structures, it may cause wrong negative judgments due to deep data inconsistencies, resulting in the interface not being updated. At this time, you need to use forceUpate() to manually force the update. 

Therefore, if the state value is a basic type such as string, number, boolean and the rendering cost is high, you can use React.PureComponent, otherwise I personally recommend using React.Component.

18. Introducing pure functions?

A function whose return result only depends on its parameters and has no side effects during execution, we call the function a pure function.

Example: Pure Function Component MsgComp

import React, { Component } from 'react';
import PropTypes from 'prop-types';

const MsgComp = ({ msg, buttonlabel, clickHandle }) => {
  return (
    <div>
      <p>{msg}</p>
      <button onClick={clickHandle}>{buttonlabel}</button>
    </div>
  );
};
MsgComp.propTypes = {
  msg: PropTypes.string,
  buttonlabel: PropTypes.string,
  clickHandle: PropTypes.func
};

class App extends Component {
  state = {
    msg: 'Hello React',
    buttonlabel: 'OK'
  }

  clickHandle = () => {
    console.log('APP CLICK');
  }

  render() {
    return (
      <div>
        <MsgComp msg={this.state.msg} buttonlabel={this.state.buttonlabel} clickHandle={this.clickHandle} />
      </div>
    );
  }
}

export default App;

19. Introducing Higher Order Components?

A higher-order component (HOC), which refers to a component that accepts a React component as input and outputs a new React component. More generally, it is described as that a high-level component is passed into a React component through a wrapping (wrapped), after a series of processing, it finally returns a relatively enhanced React component. It's just a pattern to deal with wrapping (handling) other components uniformly. Redux's connect is a high-level component.

For specific examples, please see my article: "react series (21) high-level components"

20. How to solve the problem of too deep props level?

In a typical React application, data is passed from top to bottom (from parent to child) through the props attribute. When a props parameter is shared between multiple components at multiple levels, this tree structure is from top to bottom. The following method of passing parameters is too cumbersome.

There are three ways to solve the props level is too deep:

  • Shared state with Redux.
  • useReact.createContext函数。
  • Use Content context transfer.

For specific examples, please see my article: "react series (17) passing data context across component trees"

21. How to design a state tree?

This question is an investigation of the understanding of the overall framework design of the React project, that is, the design of the framework directory structure. There are currently three main methods:

  • Design Patterns by Type

The official React Demo is such a design pattern, the directory structure is as follows:

The advantage of this division method is that the file types are clear, and the disadvantages are also obvious. It is not conducive to project module management. Each module file in the project is split into N small files and scattered in these folders. When a module has a BUG Sometimes it is often impossible to quickly find the corresponding file and solve it.

  • Design Patterns by Module

That is, a page function corresponds to a folder, and the container, component, action, reducer and other files used by this page function are all placed in this folder. The directory structure is as follows:

The advantage of this method is that the project functions are clearly divided, and each function can be divided into a small unit for independent development. But the disadvantage is that it is inconvenient to refer to each other in the public state, such as user information, Token information, etc.

  • Ducks design pattern

This design combines the advantages of the above two, and realizes the decoupling of view and state. The directory structure is as follows:

         

All view files are placed in the containers directory, and all redux files are placed in the reducers directory. They are independent of each other. Each view file can refer to the state in the reducers. In addition, there is also a components directory (upper right figure) in the module under the containers directory, which is used to place the unique components of the module. There is also a components directory (upper left picture) at the outermost layer, which is used to store public components. This framework is currently the most commonly used react state tree design pattern.

Guess you like

Origin blog.csdn.net/zeping891103/article/details/89293887