The birth of JSX
React uses JSX instead of regular JavaScript.
JSX is a JavaScript syntax extension that looks much like XML.
We don't have to use JSX, but it has the following advantages:
- JSX executes faster because it is optimized after compiling into JavaScript code.
- It is type safe and errors can be found during compilation.
- Writing templates using JSX is easier and faster.
compile
In essence, JSX is just for react The syntax provided by the createElement (component, props,... Children) method
<div className="num" index={1}> <span>123456</span> </div>
"use strict"; React.createElement("div", { className: "num", index: 1 }, React.createElement("span", null, "123456"));
The specific effect is OK Experience here
This is why although you can't see that React has been used in it, JSX will report an error if you don't introduce the module
JSX principle
From the above compiled code, the final information contained in JSX is: element tag, element attribute and child element If represented by Javascript objects:
// Omit some attributes { type: 'div' props: { className: 'num', index: 1, children: { type: 'span', props: { children: '123456' } }, } }
You can view it through the online compilation page codesandbox
So the whole process is as follows
\105748fhcfjgijkjryctcf.png)
As for why there is an intermediate step of compiling into JS objects instead of directly compiling into Dom elements
- In addition to ordinary pages, they may also be rendered to canvas or native App(React Native)
- The following diff comparison needs to be used
Native DOM API rendering process
// First create the parent label const parent = document.createElement('div') parent.className = 'num' parent.index = 1 // Create child element const child = document.createElement('span') // Create text node const text = document.createTextNode("") text.nodeValue = '123456' child.appendChild(text) parent.appendChild(child) document.body.appendChild(parent);
Create JSX object
We can roughly simulate the implementation function according to the above structure
function createElement(type, props, ...children) { return { type, props: { ...props, children } } }
However, one thing to note is that text elements have different structures, so they need to be distinguished. Considering the native process, we also need to give a corresponding structure
function createElement(type, props, ...children) { return { type, props: { ...props, children: children.map(child => typeof child === 'object' ? child : createTextElement(child) ) } } } function createTextElement(text) { return { type: "TEXT", props: { nodeValue: text, children: [] } } }
Attempt to call
createElement( "div", { className: "num", index: 1 }, createElement("span", null, "123456") )
Final operation results
{ type: 'div', props: { className: 'num', index: 1, children: [{ type: 'span', props: [{ children: [{ type: 'TEXT', props: [{ nodeValue: '123456' }] }] }] }], } }
The implementation of parsing the whole JSX syntax into JSX objects is implemented by babel. You are interested in understanding it yourself
Rendering implementation
We already know the native rendering method and JSX object results, and the rest is to generate elements by enumeration
function render(component, wrapper) { // Distinguishing labels const dom = component.type === 'TEXT' ? document.createTextNode("") : document.createElement(component.type) // Traversal props Object.keys(component.props).forEach(key => { if (key !== 'children') { dom[key] = component.props[key] } }) // Do you want to render child elements component.props.children.length && component.props.children.forEach(child => render(child, dom)) // Final mount method wrapper.appendChild(dom) }
event processing
React events are based on SyntheticEvent The instance implements the same interface as simulating cross browser native events, including stopPropagation() and preventDefault(), and expects the behavior of events to be the same across browsers Even compatible with IE8 Each SyntheicEvent object has the following properties:
boolean bubbles boolean cancelable DOMEventTarget currentTarget boolean defaultPrevented number eventPhase boolean isTrusted DOMEvent nativeEvent void preventDefault() boolean isDefaultPrevented() void stopPropagation() boolean isPropagationStopped() void persist() DOMEventTarget target number timeStamp string type
Basic science popularization
In JavaScript, event triggering essentially goes through three stages: event capture, event processing of the target object itself and event bubbling
- stopPropagation(): stop event bubbling
- preventDefault(): block default behavior
return false: in fact, you can do three things when using this
- event.preventDefault();
- event.stopPropagation();
- Stops the callback function execution and returns immediately.
How does React manage the event system?
For performance reasons React reuses the SyntheicEvent object through the pool, which means that after the event call is completed, event All attributes on target will be invalidated This means that when we try to call the react event asynchronously, because of reuse, the SyntheicEvent object will no longer exist after the event callback is executed, so we cannot access its properties
function onClick(event) { console.log(event); // => nullified object. console.log(event.type); // => "click" const eventType = event.type; // => "click" setTimeout(function() { console.log(event.type); // => null console.log(eventType); // => "click" }, 0); // Won't work. this.state.clickEvent will only contain null values. this.setState({clickEvent: event}); // You can still export event properties. this.setState({eventType: event.type}); }
A common example is the setState method
Solution
event.persist()
Calling event. in event callback Persist () method, which removes the composite event from the pool and allows user code to retain references to the event.
Cache properties
We can store event properties in event functions and pass them to asynchronous callbacks instead of accessing them directly in asynchronous callbacks
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
Debouncing a synthetic event handler
// Correct this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
Composite event registration
Source code comments
/** * Summary of `ReactBrowserEventEmitter` event handling: * * - Top-level delegation is used to trap most native browser events. This * may only occur in the main thread and is the responsibility of * ReactDOMEventListener, which is injected and can therefore support * pluggable event sources. This is the only work that occurs in the main * thread. * * - We normalize and de-duplicate events to account for browser quirks. This * may be done in the worker thread. * * - Forward these native events (with the associated top-level type used to * trap it) to `EventPluginHub`, which in turn will ask plugins if they want * to extract any synthetic events. * * - The `EventPluginHub` will then process each event by annotating them with * "dispatches", a sequence of listeners and IDs that care about that event. * * - The `EventPluginHub` then dispatches the events. * * Overview of React and the event system: * * +------------+ . * | DOM | . * +------------+ . * | . * v . * +------------+ . * | ReactEvent | . * | Listener | . * +------------+ . +-----------+ * | . +--------+|SimpleEvent| * | . | |Plugin | * +-----|------+ . v +-----------+ * | | | . +--------------+ +------------+ * | +-----------.--->|EventPluginHub| | Event | * | | . | | +-----------+ | Propagators| * | ReactEvent | . | | |TapEvent | |------------| * | Emitter | . | |<---+|Plugin | |other plugin| * | | . | | +-----------+ | utilities | * | +-----------.--->| | +------------+ * | | | . +--------------+ * +-----|------+ . ^ +-----------+ * | . | |Enter/Leave| * + . +-------+|Plugin | * +-------------+ . +-----------+ * | application | . * |-------------| . * | | . * | | . * +-------------+ . * . * React Core . General Purpose Event Plugin System */
DOM passes the event to the ReactEventListener and registers it with the document
Then distribute to specific nodes EventPluginHub is responsible for the storage of events, the creation and destruction of synthetic events and the implementation of pool mode
The following is the simulation of various types of synthetic events. The interaction converts the native DOM events into synthetic events through ReactEventEmitter, triggering the corresponding operations to be pushed into the queue for batch execution Because the browser will create an event object for each listener of each event, the above-mentioned pool reuse is to solve the problem of high memory allocation
event
Events will be automatically passed into an event object. React encapsulates the browser's native event object and provides unified API and properties
this
Because the incoming method in React is called not through object methods, but directly through function calls, the this pointed to in React is null or undefined
In general, you need to explicitly bind this point with bind or arrow function manually
Refs & DOM
This is a way to access DOM nodes or React elements created in the render method Generally used
- Processing forms, media control
- Trigger forced animation
- Integrating third-party DOM Libraries
Create Refs
class MyComponent extends React.Component { constructor(props) { super(props) this.myRef = React.createRef() } render() { return <div ref={this.myRef} /> } }
Access Refs
const node = this.myRef.current;
- If it is used for an ordinary HTMl element, react Createref () will receive the underlying DOM element as its current attribute to create a ref.
- When the ref attribute is used for a custom class component, the ref object will receive the mounted instance of the component as its current.
- You cannot use the ref attribute on functional components because they have no instances.
Callback Refs
Instead of passing the ref attribute created by createRef(), you pass a function. This function takes instances of React components or HTML DOM elements as parameters to store them and make them accessible elsewhere.
class CustomTextInput extends React.Component { constructor(props) { super(props); this.textInput = null; this.setTextInputRef = element => { this.textInput = element; }; this.focusTextInput = () => { // Use the native API directly to focus the text input box if (this.textInput) this.textInput.focus(); }; } componentDidMount() { // After rendering, the text box automatically gets focus this.focusTextInput(); } render() { // Use the callback of 'ref' to store the DOM node of the text input box in React // Instance (such as this.textInput) return ( <div> <input type="text" ref={this.setTextInputRef} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } }
If refs in the form of callback is passed between components, it is as follows:
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> ); } class Parent extends React.Component { render() { return ( <CustomTextInput inputRef={el => this.inputElement = el} /> ); } }
Used in stateless components
Because stateless components will not be instantiated, but we can use a variable to access the instance reference of the component or dom element component
function CustomTextInput(props) { let inputRef; return ( <div> <input ref={(node) => inputRef = node} /> </div> ); }